@@ -1,59 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* 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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include <stdio.h> | |||
#include <sys/time.h> | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
class Block_Timer | |||
{ | |||
unsigned long long ts; | |||
const char *prefix; | |||
unsigned long long tv_to_ts ( timeval *tv ) | |||
{ | |||
return tv->tv_sec * 1e6 + tv->tv_usec; | |||
} | |||
public: | |||
Block_Timer ( const char *prefix ) | |||
{ | |||
this->prefix = prefix; | |||
timeval tv; | |||
gettimeofday( &tv, NULL ); | |||
ts = tv_to_ts( &tv ); | |||
} | |||
~Block_Timer ( ) | |||
{ | |||
timeval tv; | |||
gettimeofday( &tv, NULL ); | |||
fprintf( stderr, "[%Lfms] %s\n", ((long double)tv_to_ts( &tv ) - ts ) / 1000, prefix ); | |||
} | |||
}; |
@@ -1,338 +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 "Client.H" | |||
#include "Port.H" | |||
#include <algorithm> | |||
#include "debug.h" | |||
#ifdef __SSE2_MATH__ | |||
#include <xmmintrin.h> | |||
#endif | |||
namespace JACK | |||
{ | |||
// nframes_t Client::_sample_rate = 0; | |||
Client::Client ( ) | |||
{ | |||
_active = false; | |||
_freewheeling = false; | |||
_zombified = false; | |||
_client = NULL; | |||
_xruns = 0; | |||
} | |||
Client::~Client ( ) | |||
{ | |||
close(); | |||
} | |||
/** Tell JACK to stop calling process callback. This MUST be called in | |||
* an inheriting class' destructor */ | |||
void | |||
Client::deactivate ( ) | |||
{ | |||
if ( _active ) | |||
jack_deactivate( _client ); | |||
_active = false; | |||
} | |||
/*******************/ | |||
/* Static Wrappers */ | |||
/*******************/ | |||
int | |||
Client::process ( nframes_t nframes, void *arg ) | |||
{ | |||
Client *c = (Client*)arg; | |||
if ( ! c->_frozen.trylock() ) | |||
return 0; | |||
int r = c->process(nframes); | |||
c->_frozen.unlock(); | |||
return r; | |||
} | |||
int | |||
Client::sync ( jack_transport_state_t state, jack_position_t *pos, void *arg ) | |||
{ | |||
return ((Client*)arg)->sync( state, pos ); | |||
} | |||
int | |||
Client::xrun ( void *arg ) | |||
{ | |||
++((Client*)arg)->_xruns; | |||
return ((Client*)arg)->xrun(); | |||
} | |||
void | |||
Client::timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *arg ) | |||
{ | |||
((Client*)arg)->timebase( state, nframes, pos, new_pos ); | |||
} | |||
void | |||
Client::freewheel ( int starting, void *arg ) | |||
{ | |||
((Client*)arg)->_freewheeling = starting; | |||
((Client*)arg)->freewheel( starting ); | |||
} | |||
int | |||
Client::buffer_size ( nframes_t nframes, void *arg ) | |||
{ | |||
return ((Client*)arg)->buffer_size( nframes ); | |||
} | |||
void | |||
Client::thread_init ( void *arg ) | |||
{ | |||
#ifdef __SSE2_MATH__ | |||
/* set FTZ and DAZ flags */ | |||
_mm_setcsr(_mm_getcsr() | 0x8040); | |||
#endif | |||
((Client*)arg)->thread_init(); | |||
} | |||
void | |||
Client::latency ( jack_latency_callback_mode_t mode, void *arg ) | |||
{ | |||
((Client*)arg)->latency( mode ); | |||
} | |||
void | |||
Client::shutdown ( void *arg ) | |||
{ | |||
((Client*)arg)->_zombified = true; | |||
((Client*)arg)->shutdown(); | |||
} | |||
void | |||
Client::port_connect ( jack_port_id_t a, jack_port_id_t b, int connect, void *arg ) | |||
{ | |||
Client *c = (Client*)arg; | |||
if (! c->_frozen.trylock() ) | |||
return; | |||
((Client*)arg)->port_connect( a, b, connect ); | |||
c->_frozen.unlock(); | |||
} | |||
int | |||
Client::sample_rate_changed ( nframes_t srate, void *arg ) | |||
{ | |||
// ((Client*)arg)->_sample_rate = srate; | |||
return ((Client*)arg)->sample_rate_changed( srate ); | |||
} | |||
void | |||
Client::activate ( void ) | |||
{ | |||
jack_activate( _client ); | |||
_active = true; | |||
} | |||
/** Connect to JACK using client name /client_name/. Return a static | |||
* pointer to actual name as reported by JACK */ | |||
const char * | |||
Client::init ( const char *client_name, unsigned int opts ) | |||
{ | |||
if (( _client = jack_client_open ( client_name, (jack_options_t)0, NULL )) == 0 ) | |||
return NULL; | |||
#define set_callback( name ) jack_set_ ## name ## _callback( _client, &Client:: name , this ) | |||
set_callback( thread_init ); | |||
set_callback( process ); | |||
set_callback( xrun ); | |||
set_callback( freewheel ); | |||
set_callback( buffer_size ); | |||
set_callback( port_connect ); | |||
jack_set_sample_rate_callback( _client, &Client::sample_rate_changed, this ); | |||
#ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
set_callback( latency ); | |||
#endif | |||
/* FIXME: should we wait to register this until after the project | |||
has been loaded (and we have disk threads running)? */ | |||
if ( opts & SLOW_SYNC ) | |||
set_callback( sync ); | |||
if ( opts & TIMEBASE_MASTER ) | |||
jack_set_timebase_callback( _client, 0, &Client::timebase, this ); | |||
jack_on_shutdown( _client, &Client::shutdown, this ); | |||
activate(); | |||
// _sample_rate = frame_rate(); | |||
return jack_get_client_name( _client ); | |||
} | |||
/* THREAD: RT */ | |||
/** enter or leave freehweeling mode */ | |||
void | |||
Client::freewheeling ( bool yes ) | |||
{ | |||
if ( jack_set_freewheel( _client, yes ) ) | |||
; | |||
// WARNING( "Unkown error while setting freewheeling mode" ); | |||
} | |||
const char * | |||
Client::jack_name ( void ) const | |||
{ | |||
return jack_get_client_name( _client ); | |||
} | |||
void | |||
Client::port_added ( Port *p ) | |||
{ | |||
std::list < JACK::Port * >::iterator i = std::find( _active_ports.begin(), _active_ports.end(), p ); | |||
if ( i != _active_ports.end() ) | |||
return; | |||
_active_ports.push_back( p ); | |||
} | |||
void | |||
Client::port_removed ( Port *p ) | |||
{ | |||
_active_ports.remove( p ); | |||
} | |||
void | |||
Client::freeze_ports ( void ) | |||
{ | |||
for ( std::list < JACK::Port * >::iterator i = _active_ports.begin(); | |||
i != _active_ports.end(); | |||
++i ) | |||
{ | |||
(*i)->freeze(); | |||
} | |||
} | |||
void | |||
Client::thaw_ports ( void ) | |||
{ | |||
/* Sort ports for the sake of clients (e.g. patchage), for | |||
* whom the order of creation may matter (for display) */ | |||
_active_ports.sort(); | |||
for ( std::list < JACK::Port * >::iterator i = _active_ports.begin(); | |||
i != _active_ports.end(); | |||
++i ) | |||
{ | |||
(*i)->thaw(); | |||
} | |||
} | |||
void | |||
Client::close ( void ) | |||
{ | |||
deactivate(); | |||
if ( _client ) | |||
{ | |||
DMESSAGE( "Closing JACK client" ); | |||
jack_client_close( _client ); | |||
} | |||
_client = NULL; | |||
} | |||
const char * | |||
Client::name ( const char *s ) | |||
{ | |||
/* Because the JACK API does not provide a mechanism for renaming | |||
* clients, we have to save connections, destroy our client, | |||
* create a client with the new name, and restore our | |||
* connections. Lovely. */ | |||
freeze_ports(); | |||
jack_deactivate( _client ); | |||
jack_client_close( _client ); | |||
_client = NULL; | |||
_frozen.lock(); | |||
s = init( s ); | |||
thaw_ports(); | |||
_frozen.unlock(); | |||
return s; | |||
} | |||
void | |||
Client::recompute_latencies ( void ) | |||
{ | |||
jack_recompute_total_latencies( _client ); | |||
} | |||
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 ); | |||
} | |||
} |
@@ -1,130 +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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#ifdef HAVE_JACK_METADATA | |||
# include <jack/metadata.h> | |||
#endif | |||
#include <Mutex.H> | |||
typedef jack_nframes_t nframes_t; | |||
typedef jack_default_audio_sample_t sample_t; | |||
#include <list> | |||
namespace JACK | |||
{ | |||
class Port; | |||
class Client | |||
{ | |||
std::list <JACK::Port*> _active_ports; | |||
Mutex _frozen; | |||
jack_client_t *_client; | |||
// nframes_t _sample_rate; | |||
volatile int _xruns; | |||
volatile bool _freewheeling; | |||
volatile bool _zombified; | |||
volatile bool _active; | |||
static int sample_rate_changed ( nframes_t srate, void *arg ); | |||
virtual int sample_rate_changed ( nframes_t srate ) { return 0; } | |||
static void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect, void *arg ); | |||
virtual void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect ) { } | |||
static void shutdown ( void *arg ); | |||
virtual void shutdown ( void ) = 0; | |||
static int process ( nframes_t nframes, void *arg ); | |||
virtual int process ( nframes_t nframes ) = 0; | |||
static int sync ( jack_transport_state_t state, jack_position_t *pos, void *arg ); | |||
virtual int sync ( jack_transport_state_t, jack_position_t * ) { return 1; } | |||
static int xrun ( void *arg ); | |||
virtual int xrun ( void ) = 0; | |||
static void timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *arg ); | |||
virtual void timebase ( jack_transport_state_t, jack_nframes_t, jack_position_t *, int ) { } | |||
static void freewheel ( int yes, void *arg ); | |||
virtual void freewheel ( bool yes ) = 0; | |||
static int buffer_size ( nframes_t nframes, void *arg ); | |||
virtual int buffer_size ( nframes_t nframes ) = 0; | |||
static void thread_init ( void *arg ); | |||
virtual void thread_init ( void ) = 0; | |||
static void latency ( jack_latency_callback_mode_t mode, void *arg ); | |||
virtual void latency ( jack_latency_callback_mode_t mode ) { } | |||
Client ( const Client &rhs ); | |||
Client & operator = ( const Client &rhs ); | |||
void freeze_ports ( void ); | |||
void thaw_ports ( void ); | |||
protected: | |||
bool active ( void ) const { return _active; } | |||
void deactivate ( void ); | |||
void activate ( void ); | |||
private: | |||
friend class Port; | |||
friend class Transport; | |||
public: | |||
enum options { DEFAULT = 0, | |||
SLOW_SYNC = 1 << 0, | |||
TIMEBASE_MASTER = 1 << 1 }; | |||
jack_client_t * jack_client ( void ) const { return _client; } | |||
void port_added ( JACK::Port * p ); | |||
void port_removed ( JACK::Port *p ); | |||
Client ( ); | |||
virtual ~Client ( ); | |||
const char * init ( const char *client_name, unsigned int opts = 0 ); | |||
const char * name ( const char * ); | |||
const char *jack_name ( void ) const; | |||
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 ); } | |||
nframes_t sample_rate ( void ) const { return jack_get_sample_rate( _client ); } | |||
int xruns ( void ) const { return _xruns; }; | |||
bool freewheeling ( void ) const { return _freewheeling; } | |||
void freewheeling ( bool yes ); | |||
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 ); | |||
void recompute_latencies ( void ); | |||
static int maximum_name_length ( void ) { return jack_client_name_size(); } | |||
}; | |||
} |
@@ -1,456 +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. */ | |||
/*******************************************************************************/ | |||
/* Wrapper for a JACK audio port */ | |||
#include "Port.H" | |||
#include <string.h> | |||
#include <stdio.h> // sprintf | |||
#include <errno.h> | |||
#include <assert.h> | |||
#include "debug.h" | |||
namespace JACK | |||
{ | |||
/* static char *name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ); */ | |||
int | |||
Port::max_name ( void ) | |||
{ | |||
return jack_port_name_size() - jack_client_name_size() - 6; | |||
} | |||
Port::Port ( const Port &rhs ) | |||
{ | |||
_connections = NULL; | |||
_terminal = rhs._terminal; | |||
// _connections = rhs._connections; | |||
_client = rhs._client; | |||
_port = rhs._port; | |||
_direction = rhs._direction; | |||
_type = rhs._type; | |||
_name = NULL; | |||
_name = strdup( rhs._name ); | |||
_trackname = NULL; | |||
if ( rhs._trackname ) | |||
_trackname = strdup( rhs._trackname ); | |||
_client->port_added( this ); | |||
} | |||
/* nframes is the number of frames to buffer */ | |||
Port::Port ( JACK::Client *client, jack_port_t *port ) | |||
{ | |||
_terminal = 0; | |||
_connections = NULL; | |||
_client = client; | |||
_port = port; | |||
_name = strdup( jack_port_name( port ) ); | |||
_trackname = NULL; | |||
_direction = ( jack_port_flags( _port ) & JackPortIsOutput ) ? Output : Input; | |||
const char *type = jack_port_type( _port ); | |||
_type = Audio; | |||
if ( strstr( type, "MIDI") ) | |||
_type = MIDI; | |||
else if ( strstr( type, "CV)") ) | |||
_type = CV; | |||
_client->port_added( this ); | |||
} | |||
Port::Port ( JACK::Client *client, const char *trackname, const char *name, direction_e dir, type_e type ) | |||
{ | |||
_port = 0; | |||
_terminal = 0; | |||
_name = NULL; | |||
_trackname = NULL; | |||
_connections = NULL; | |||
_client = client; | |||
_direction = dir; | |||
_type = type; | |||
_trackname = NULL; | |||
if ( trackname ) | |||
_trackname = strdup( trackname ); | |||
_name = strdup( name ); | |||
_client->port_added( this ); | |||
} | |||
/* Port::Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype ) */ | |||
/* { */ | |||
/* _port = 0; */ | |||
/* _terminal = 0; */ | |||
/* _name = NULL; */ | |||
/* _connections = NULL; */ | |||
/* _client = client; */ | |||
/* _name = name_for_port( dir, base, n, subtype ); */ | |||
/* _direction = dir; */ | |||
/* _type = type; */ | |||
/* _client->port_added( this ); */ | |||
/* } */ | |||
/* Port::Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype ) */ | |||
/* { */ | |||
/* _port = 0; */ | |||
/* _terminal = 0; */ | |||
/* _name = NULL; */ | |||
/* _connections = NULL; */ | |||
/* _client = client; */ | |||
/* _name = name_for_port( dir, NULL, n, subtype ); */ | |||
/* _direction = dir; */ | |||
/* _type = type; */ | |||
/* _client->port_added( this ); */ | |||
/* } */ | |||
Port::~Port ( ) | |||
{ | |||
_client->port_removed( this ); | |||
if ( _name ) | |||
{ | |||
free( _name ); | |||
_name = NULL; | |||
} | |||
if ( _trackname ) | |||
{ | |||
free( _trackname ); | |||
_trackname = NULL; | |||
} | |||
} | |||
/* sort input before output and then by alpha */ | |||
bool | |||
Port::operator < ( const Port & rhs ) const | |||
{ | |||
if ( type() == rhs.type() ) | |||
return strcmp( name(), rhs.name() ); | |||
else | |||
return direction() == Port::Input; | |||
} | |||
/* static char * */ | |||
/* name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ) */ | |||
/* { */ | |||
/* char *pname; */ | |||
/* const char *dir_s = dir == Port::Output ? "out" : "in"; */ | |||
/* if ( type ) */ | |||
/* asprintf( &pname, "%s-%s%s%s-%d", type, base ? base : "", base ? "/" : "", dir_s, n + 1 ); */ | |||
/* else */ | |||
/* asprintf( &pname, "%s%s%s-%d", base ? base : "", base ? "/" : "", dir_s, n + 1 ); */ | |||
/* return pname; */ | |||
/* } */ | |||
bool | |||
Port::activate ( void ) | |||
{ | |||
/* assert( !_port ); */ | |||
int flags = 0; | |||
if ( _direction == Output ) | |||
flags |= JackPortIsOutput; | |||
else | |||
flags |= JackPortIsInput; | |||
if ( _terminal ) | |||
flags |= JackPortIsTerminal; | |||
char jackname[max_name()]; | |||
snprintf( jackname, sizeof(jackname), "%s%s%s", _trackname ? _trackname : "", _trackname ? "/" : "", _name ); | |||
DMESSAGE( "Activating port name %s", jackname ); | |||
_port = jack_port_register( _client->jack_client(), jackname, | |||
( _type == Audio ) || ( _type == CV ) ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE, | |||
flags, | |||
0 ); | |||
#ifdef HAVE_JACK_METADATA | |||
if ( _type == CV ) | |||
{ | |||
jack_uuid_t uuid = jack_port_uuid( _port ); | |||
jack_set_property( _client->jack_client(), uuid, "http://jackaudio.org/metadata/signal-type", "CV", "text/plain" ); | |||
} | |||
#endif | |||
DMESSAGE( "Port = %p", _port ); | |||
if ( ! _port ) | |||
return false; | |||
_client->port_added( this ); | |||
return true; | |||
} | |||
/** returns the sum of latency of all ports between this one and a | |||
terminal port. */ | |||
nframes_t | |||
Port::total_latency ( void ) const | |||
{ | |||
#ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
jack_latency_range_t range; | |||
jack_port_get_latency_range( _port, _direction == Input ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
return range.max; | |||
#else | |||
return jack_port_get_total_latency( _client->jack_client() , _port ); | |||
#endif | |||
} | |||
/** returns the number of frames of latency assigned to this port */ | |||
void | |||
Port::get_latency ( direction_e dir, nframes_t *min, nframes_t *max ) const | |||
{ | |||
#ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
jack_latency_range_t range; | |||
jack_port_get_latency_range( _port, dir == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
*min = range.min; | |||
*max = range.max; | |||
#else | |||
*min = *max = jack_port_get_latency( _port ); | |||
#endif | |||
} | |||
/** inform JACK that port has /frames/ frames of latency */ | |||
void | |||
Port::set_latency ( direction_e dir, nframes_t min, nframes_t max ) | |||
{ | |||
#ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE | |||
jack_latency_range_t range; | |||
// DMESSAGE( "Setting port latency!" ); | |||
range.max = max; | |||
range.min = min; | |||
jack_port_set_latency_range( _port, dir == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); | |||
#else | |||
jack_port_set_latency( _port, max ); | |||
#endif | |||
} | |||
void | |||
Port::shutdown ( void ) | |||
{ | |||
deactivate(); | |||
_client->port_removed( this ); | |||
} | |||
void | |||
Port::deactivate ( void ) | |||
{ | |||
if ( _port ) | |||
{ | |||
#ifdef HAVE_JACK_METADATA | |||
if ( _type == CV ) | |||
{ | |||
jack_uuid_t uuid = jack_port_uuid(_port); | |||
jack_remove_property(_client->jack_client(), uuid, "http://jackaudio.org/metadata/signal-type"); | |||
} | |||
#endif | |||
jack_port_unregister( _client->jack_client(), _port ); | |||
} | |||
_port = 0; | |||
} | |||
void | |||
Port::name ( const char *name ) | |||
{ | |||
if ( _name ) | |||
free( _name ); | |||
_name = strdup( name ); | |||
} | |||
void | |||
Port::trackname ( const char *trackname ) | |||
{ | |||
if ( _trackname ) | |||
free( _trackname ); | |||
_trackname = NULL; | |||
if ( trackname ) | |||
_trackname = strdup( trackname ); | |||
} | |||
bool | |||
Port::rename ( void ) | |||
{ | |||
char jackname[max_name()]; | |||
snprintf( jackname, sizeof(jackname), "%s%s%s", _trackname ? _trackname : "", _trackname ? "/" : "", _name ); | |||
if ( _port ) | |||
return 0 == jack_port_set_name( _port, jackname ); | |||
else | |||
return false; | |||
} | |||
void | |||
Port::write ( sample_t *buf, nframes_t nframes ) | |||
{ | |||
memcpy( buffer( nframes ), buf, nframes * sizeof( sample_t ) ); | |||
} | |||
void | |||
Port::read ( sample_t *buf, nframes_t nframes ) | |||
{ | |||
memcpy( buf, buffer( nframes ), nframes * sizeof( sample_t ) ); | |||
} | |||
void * | |||
Port::buffer ( nframes_t nframes ) | |||
{ | |||
return jack_port_get_buffer( _port, nframes ); | |||
} | |||
void | |||
Port::silence ( nframes_t nframes ) | |||
{ | |||
memset( buffer( nframes ), 0, nframes * sizeof( sample_t ) ); | |||
} | |||
/** Return a malloc()'d null terminated array of strings | |||
* representing all ports to which this port is connected. */ | |||
const char ** | |||
Port::connections ( void ) | |||
{ | |||
ASSERT( _port, "Attempt to get connections of null port" ); | |||
return jack_port_get_connections( _port ); | |||
} | |||
/** Restore the connections returned by connections() */ | |||
bool | |||
Port::connections ( const char **port_names ) | |||
{ | |||
if ( ! port_names ) | |||
return true; | |||
for ( const char **port_name = port_names; *port_name; ++port_name ) | |||
{ | |||
printf( "Attempting to reconnect to %s\n", *port_name ); | |||
connect( *port_name ); | |||
} | |||
return true; | |||
} | |||
int | |||
Port::connect ( const char *to ) | |||
{ | |||
const char *name = jack_port_name( _port ); | |||
/* jack complains when you attempt to connect an already connected port... */ | |||
if ( connected_to( to ) ) | |||
return 0; | |||
if ( _direction == Output ) | |||
{ | |||
DMESSAGE("Connecting jack port %s to %s", name, to ); | |||
return jack_connect( _client->jack_client(), name, to ); | |||
} | |||
else | |||
{ | |||
DMESSAGE("Connecting jack port %s to %s", to, name ); | |||
return jack_connect( _client->jack_client(), to, name ); | |||
} | |||
} | |||
int | |||
Port::disconnect ( const char *from ) | |||
{ | |||
const char *name = jack_port_name( _port ); | |||
if ( _direction == Output ) | |||
{ | |||
DMESSAGE("Disconnecting jack port %s from %s", name, from ); | |||
return jack_disconnect( _client->jack_client(), name, from ); | |||
} | |||
else | |||
{ | |||
DMESSAGE("Disconnecting jack port %s from %s", from, name ); | |||
return jack_disconnect( _client->jack_client(), from, name ); | |||
} | |||
} | |||
bool | |||
Port::connected_to ( const char *to ) | |||
{ | |||
return jack_port_connected_to( _port, to ); | |||
} | |||
void | |||
Port::freeze ( void ) | |||
{ | |||
if ( _connections ) | |||
free( _connections ); | |||
// DMESSAGE( "Freezing port %s", _name ); | |||
_connections = connections(); | |||
// deactivate(); | |||
} | |||
void | |||
Port::thaw ( void ) | |||
{ | |||
// DMESSAGE( "Thawing port %s", _name ); | |||
activate(); | |||
if ( _connections ) | |||
{ | |||
connections( _connections ); | |||
free( _connections ); | |||
_connections = NULL; | |||
} | |||
} | |||
} |
@@ -1,115 +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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
// #include <jack/jack.h> | |||
#include "Client.H" | |||
#include <stdlib.h> | |||
namespace JACK | |||
{ | |||
class Port | |||
{ | |||
jack_port_t *_port; | |||
char *_trackname; | |||
char *_name; | |||
JACK::Client *_client; | |||
/* FIXME: reference count? */ | |||
/* /\* not permitted *\/ */ | |||
/* Port ( const Port &rhs ); */ | |||
/* Port & operator= ( const Port &rhs ); */ | |||
public: | |||
bool operator < ( const Port & rhs ) const; | |||
enum direction_e { Output, Input }; | |||
enum type_e { Audio, MIDI, CV }; | |||
static int max_name ( void ); | |||
Port ( JACK::Client *client, jack_port_t *port ); | |||
/* Port ( JACK::Client *client, const char *name, direction_e dir, type_e type ); */ | |||
Port ( JACK::Client *client, const char *trackname, const char *name, direction_e dir, type_e type ); | |||
/* Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype=0 ); */ | |||
/* Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype=0 ); */ | |||
// Port ( ); | |||
~Port ( ); | |||
Port ( const Port & rhs ); | |||
bool valid ( void ) const { return _port; } | |||
bool connected ( void ) const { return jack_port_connected( _port ); } | |||
direction_e direction ( void ) const { return _direction; } | |||
type_e type ( void ) const { return _type; } | |||
const char * name ( void ) const { return _name; } | |||
const char * trackname ( void ) const { return _trackname; } | |||
void name ( const char *name ); | |||
void trackname ( const char *trackname ); | |||
bool rename ( void ); | |||
const char * jack_name ( void ) const { return jack_port_name( _port ); } | |||
// bool name ( const char *base, int n, const char *type=0 ); | |||
nframes_t total_latency ( void ) const; | |||
void get_latency ( direction_e dir, nframes_t *min, nframes_t *max ) const; | |||
/* it's only valid to call this in a latency callback! */ | |||
void set_latency ( direction_e dir, nframes_t min, nframes_t max ); | |||
void terminal ( bool b ) { _terminal = b; } | |||
bool activate ( void ); | |||
void shutdown ( void ); | |||
void write ( sample_t *buf, nframes_t nframes ); | |||
void read ( sample_t *buf, nframes_t nframes ); | |||
void *buffer ( nframes_t nframes ); | |||
void silence ( nframes_t nframes ); | |||
int connect ( const char *to ); | |||
int disconnect ( const char *from ); | |||
bool connected_to ( const char *to ); | |||
/* */ | |||
const char ** connections ( void ); | |||
bool connections ( const char **port_names ); | |||
void freeze ( void ); | |||
void thaw ( void ); | |||
JACK::Client * client ( void ) const { return _client; } | |||
void client ( JACK::Client *c ) { _client = c; } | |||
private: | |||
friend class Client; | |||
direction_e _direction; | |||
type_e _type; | |||
bool _terminal; | |||
void deactivate ( void ); | |||
/* bool activate ( const char *name, direction_e dir ); */ | |||
const char **_connections; | |||
}; | |||
} |
@@ -1,17 +0,0 @@ | |||
# -*- mode: makefile; -*- | |||
nonlib_SRCS := $(wildcard JACK/*.C LASH/*.C) | |||
nonlib_SRCS:=$(sort $(nonlib_SRCS)) | |||
nonlib_OBJS:=$(nonlib_SRCS:.C=.o) | |||
all: nonlib/libnonlib.a | |||
nonlib/libnonlib.a: $(nonlib_OBJS) | |||
@ ar rcs $@ $(nonlib_OBJS) | |||
.PHONEY: nonlib | |||
nonlib: nonlib/libnonlib.a | |||
nonlib_clean: | |||
rm -f $(nonlib_OBJS) nonlib/libnonlib.a |
@@ -1,308 +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 "Log_Entry.H" | |||
// #include "const.h" | |||
#include "debug.h" | |||
Log_Entry::Log_Entry ( ) | |||
{ | |||
_sa = (char**)malloc( sizeof( char * ) ); | |||
*_sa = NULL; | |||
_i = 0; | |||
} | |||
Log_Entry::Log_Entry ( char **sa ) | |||
{ | |||
_sa = sa; | |||
_i = 0; | |||
if ( _sa ) | |||
while ( _sa[ _i ] ) ++_i; | |||
} | |||
Log_Entry::Log_Entry ( const char *s ) | |||
{ | |||
_i = 0; | |||
_sa = s ? parse_alist( s ) : NULL; | |||
if ( _sa ) | |||
while ( _sa[ _i ] ) ++_i; | |||
} | |||
Log_Entry::~Log_Entry ( ) | |||
{ | |||
if ( ! _sa ) | |||
return; | |||
for ( _i = 0; _sa[ _i ]; ++_i ) | |||
{ | |||
free( _sa[ _i ] ); | |||
} | |||
free( _sa ); | |||
} | |||
/** remove escapes from string /s/ in-place */ | |||
static void | |||
unescape ( char *s ) | |||
{ | |||
char *r = s; | |||
for ( ; *s; s++, r++ ) | |||
{ | |||
if ( '\\' == *s ) | |||
{ | |||
switch ( *(++s) ) | |||
{ | |||
case 'n': | |||
*r = '\n'; | |||
break; | |||
case '"': | |||
*r = '"'; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
else | |||
*r = *s; | |||
} | |||
*r = '\0'; | |||
} | |||
/** return a dynamically allocated string representing this log entry */ | |||
char * | |||
Log_Entry::print ( void ) const | |||
{ | |||
/* FIXME: gross over-allocation */ | |||
char *r = (char*)malloc( 1024 ); | |||
r[0] = 0; | |||
for ( int i = 0; i < size(); ++i ) | |||
{ | |||
const char *s, *v; | |||
get( i, &s, &v ); | |||
/* FIXME: arbitrary limit */ | |||
char t[1024]; | |||
snprintf( t, sizeof( t ), "%s %s%s", s, v, size() == i + 1 ? "" : " " ); | |||
strcat( r, t ); | |||
} | |||
char *r2 = (char*)malloc( strlen( r ) + 1 ); | |||
strcpy( r2, r ); | |||
free( r ); | |||
return r2; | |||
} | |||
/** sigh. parse a string of ":name value :name value" pairs into an | |||
* array of strings, one per pair */ | |||
// FIXME: doesn't handle the case of :name ":foo bar", nested quotes | |||
// or other things it should. | |||
char ** | |||
Log_Entry::parse_alist( const char *s ) | |||
{ | |||
// FIXME: bogus over allocation... | |||
int tl = strlen( s ); | |||
char **r = (char**)malloc( sizeof( char* ) * tl ); | |||
bool quote = false; | |||
bool value = false; | |||
const char *c = NULL; | |||
int i = 0; | |||
for ( ; ; s++ ) | |||
{ | |||
switch ( *s ) | |||
{ | |||
case '\0': | |||
case ' ': | |||
if ( ! quote && c ) | |||
{ | |||
if ( ! value ) | |||
{ | |||
value = true; | |||
break; | |||
} | |||
int l = s - c; | |||
char *pair = (char*)malloc( l + 1 ); | |||
/* remove trailing space */ | |||
if ( c[ l - 1 ] == ' ' ) | |||
--l; | |||
strncpy( pair, c, l ); | |||
pair[ l ] = '\0'; | |||
r[ i++ ] = pair; | |||
/* split */ | |||
strtok( pair, " " ); | |||
/* remove quotes */ | |||
char *v = pair + strlen( pair ) + 1; | |||
unescape( v ); | |||
if ( *v == '"' ) | |||
{ | |||
// v++; | |||
if ( v[ strlen( v ) - 1 ] != '"' ) | |||
WARNING( "invalid quoting in log entry!" ); | |||
else | |||
{ | |||
v[ strlen( v ) - 1 ] = '\0'; | |||
memmove( v, v + 1, strlen( v ) + 1 ); | |||
} | |||
} | |||
c = NULL; | |||
} | |||
break; | |||
case ':': /* this is a key */ | |||
if ( ! quote && ! c ) | |||
{ | |||
c = s; | |||
value = false; | |||
} | |||
break; | |||
case '"': | |||
quote = !quote; | |||
break; | |||
case '\\': | |||
s++; | |||
break; | |||
} | |||
if ( *s == '\0' ) | |||
break; | |||
} | |||
r[ i ] = NULL; | |||
return r; | |||
} | |||
/** compare elements of dumps s1 and s2, removing those elements | |||
of dst which are not changed from src */ | |||
bool | |||
Log_Entry::diff ( Log_Entry *e1, Log_Entry *e2 ) | |||
{ | |||
if ( ! e1 ) | |||
return true; | |||
char **sa1 = e1->_sa; | |||
char **sa2 = e2->_sa; | |||
if ( ! sa1 ) | |||
return true; | |||
int w = 0; | |||
for ( int i = 0; sa1[ i ]; ++i ) | |||
{ | |||
const char *v1 = sa1[ i ] + strlen( sa1[ i ] ) + 1; | |||
const char *v2 = sa2[ i ] + strlen( sa2[ i ] ) + 1; | |||
if ( ! strcmp( sa1[ i ], sa2[ i ] ) && ! strcmp( v1, v2 ) ) | |||
{ | |||
free( sa2[ i ] ); | |||
free( sa1[ i ] ); | |||
} | |||
else | |||
{ | |||
sa2[ w ] = sa2[ i ]; | |||
sa1[ w ] = sa1[ i ]; | |||
w++; | |||
} | |||
} | |||
sa1[ w ] = NULL; | |||
sa2[ w ] = NULL; | |||
e1->_i = w; | |||
e2->_i = w; | |||
return w == 0 ? false : true; | |||
} | |||
void | |||
Log_Entry::grow ( ) | |||
{ | |||
_sa = (char**)realloc( _sa, sizeof( char * ) * (_i + 2) ); | |||
_sa[ _i + 1 ] = NULL; | |||
} | |||
int | |||
Log_Entry::size ( void ) const | |||
{ | |||
return _i; | |||
} | |||
void | |||
Log_Entry::get ( int n, const char **name, const char **value ) const | |||
{ | |||
*name = _sa[ n ]; | |||
*value = *name + strlen( *name ) + 1; | |||
} | |||
void | |||
Log_Entry::remove ( const char *name ) | |||
{ | |||
for ( int i = 0; i < _i; i++ ) | |||
{ | |||
if ( !strcmp( _sa[ i ], name ) ) | |||
{ | |||
free( _sa[i] ); | |||
_sa[i] = NULL; | |||
} | |||
} | |||
} | |||
char ** | |||
Log_Entry::sa ( void ) | |||
{ | |||
return _sa; | |||
/* char **sa = _sa; */ | |||
/* // _sa = NULL; */ | |||
/* return sa; */ | |||
/* } */ | |||
} |
@@ -1,104 +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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include "Loggable.H" | |||
#include "types.h" | |||
class Log_Entry | |||
{ | |||
// vector <Pair> _sa; | |||
char **_sa; | |||
int _i; | |||
/* not permitted */ | |||
Log_Entry ( const Log_Entry &rhs ); | |||
Log_Entry & operator= ( const Log_Entry &rhs ); | |||
static char ** parse_alist ( const char *s ); | |||
static bool log_diff ( char **sa1, char **sa2 ); | |||
public: | |||
Log_Entry ( ); | |||
Log_Entry ( char **sa ); | |||
Log_Entry ( const char *s ); | |||
~Log_Entry ( ); | |||
/****************/ | |||
/* Construction */ | |||
/****************/ | |||
void grow ( ); | |||
#define ADD( type, format, exp ) \ | |||
void add ( const char *name, type v ) \ | |||
{ \ | |||
grow(); \ | |||
(void)asprintf( &_sa[ _i ], "%s " format, name, (exp) ); \ | |||
strtok( _sa[ _i++ ], " " ); \ | |||
} | |||
void add_raw ( const char *name, const char *v ) | |||
{ | |||
grow(); | |||
(void)asprintf( &_sa[ _i ], "%s %s", name, v ); | |||
strtok( _sa[ _i++ ], " " ); | |||
} | |||
/***************/ | |||
/* Examination */ | |||
/***************/ | |||
static bool diff ( Log_Entry *e1, Log_Entry *e2 ); | |||
int size ( void ) const; | |||
void get ( int n, const char **name, const char **value ) const; | |||
char **sa ( void ); | |||
char *print ( void ) const; | |||
/* #define ADD ( type, format, exp ) \ */ | |||
/* void add ( const char *name, type v ) \ */ | |||
/* { \ */ | |||
/* char pat[ 256 ]; \ */ | |||
/* Pair p; \ */ | |||
/* p.name = strdup( name ); \ */ | |||
/* snprintf( pat, sizeof( pat ), format, exp ); \ */ | |||
/* p.value = strdup( pat ); \ */ | |||
/* _sa.push( p ); \ */ | |||
/* } \ */ | |||
void remove ( const char *s ); | |||
ADD( int, "%d", v ); | |||
ADD( nframes_t, "%lu", (unsigned long)v ); | |||
ADD( unsigned long, "%lu", v ); | |||
ADD( const char *, "\"%s\"", v ? Loggable::escape( v ) : "" ); | |||
ADD( Loggable * , "0x%X", v ? v->id() : 0 ); | |||
ADD( float, "%f", v ); | |||
ADD( double, "%f", v ); | |||
#undef ADD | |||
}; |
@@ -1,906 +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. */ | |||
/*******************************************************************************/ | |||
/* This class handles all journaling. All journaled objects must | |||
inherit from Loggable as well as define a few special methods (via | |||
macros), get and set methods, and have contructors and destructors | |||
that call log_create() and log_destroy() in the appropriate | |||
order. Any action that might affect multiple loggable objects | |||
*must* be braced by calls to Loggable::block_start() and | |||
Loggable::block_end() in order for Undo to work properly. */ | |||
#include "Loggable.H" | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include "file.h" | |||
// #include "const.h" | |||
#include "debug.h" | |||
#include "Mutex.H" | |||
#include <algorithm> | |||
using std::min; | |||
using std::max; | |||
#ifndef NDEBUG | |||
bool Loggable::_snapshotting = false; | |||
int Loggable::_snapshot_count = 0; | |||
#endif | |||
bool Loggable::_readonly = false; | |||
FILE *Loggable::_fp; | |||
unsigned int Loggable::_log_id = 0; | |||
int Loggable::_level = 0; | |||
int Loggable::_dirty = 0; | |||
off_t Loggable::_undo_offset = 0; | |||
std::map <unsigned int, Loggable::log_pair > Loggable::_loggables; | |||
std::map <std::string, create_func*> Loggable::_class_map; | |||
std::queue <char *> Loggable::_transaction; | |||
progress_func *Loggable::_progress_callback = NULL; | |||
void *Loggable::_progress_callback_arg = NULL; | |||
snapshot_func *Loggable::_snapshot_callback = NULL; | |||
void *Loggable::_snapshot_callback_arg = NULL; | |||
dirty_func *Loggable::_dirty_callback = NULL; | |||
void *Loggable::_dirty_callback_arg = NULL; | |||
static Mutex _lock; | |||
Loggable::~Loggable ( ) | |||
{ | |||
Locker lock( _lock );; | |||
_loggables[ _id ].loggable = NULL; | |||
} | |||
void | |||
Loggable::block_start ( void ) | |||
{ | |||
Locker lock( _lock );; | |||
++Loggable::_level; | |||
} | |||
void | |||
Loggable::block_end ( void ) | |||
{ | |||
Locker lock( _lock );; | |||
--Loggable::_level; | |||
ASSERT( Loggable::_level >= 0, "Programming error" ); | |||
if ( Loggable::_level == 0 ) | |||
flush(); | |||
} | |||
Loggable * | |||
Loggable::find ( unsigned int id ) | |||
{ | |||
if ( _relative_id ) | |||
id += _relative_id; | |||
return _loggables[ id ].loggable; | |||
} | |||
/** Open the journal /filename/ and replay it, bringing the end state back into RAM */ | |||
bool | |||
Loggable::open ( const char *filename ) | |||
{ | |||
FILE *fp; | |||
Loggable::_fp = NULL; | |||
if ( ! ( fp = fopen( filename, "a+" ) ) ) | |||
{ | |||
WARNING( "Could not open log file for writing!" ); | |||
if ( ! ( fp = fopen( filename, "r" ) ) ) | |||
{ | |||
WARNING( "Could not open log file for reading!" ); | |||
return false; | |||
} | |||
else | |||
{ | |||
_readonly = true; | |||
} | |||
} | |||
_readonly = false; | |||
load_unjournaled_state(); | |||
if ( newer( "snapshot", filename ) ) | |||
{ | |||
MESSAGE( "Loading snapshot" ); | |||
FILE *fp = fopen( "snapshot", "r" ); | |||
replay( fp ); | |||
fclose( fp ); | |||
} | |||
else | |||
{ | |||
MESSAGE( "Replaying journal" ); | |||
replay( fp ); | |||
} | |||
fseek( fp, 0, SEEK_END ); | |||
_undo_offset = ftell( fp ); | |||
Loggable::_fp = fp; | |||
return true; | |||
} | |||
bool | |||
Loggable::load_unjournaled_state ( void ) | |||
{ | |||
FILE *fp; | |||
fp = fopen( "unjournaled", "r" ); | |||
if ( ! fp ) | |||
{ | |||
DWARNING( "Could not open unjournaled state file for reading" ); | |||
return false; | |||
} | |||
unsigned int id; | |||
char *buf; | |||
while ( fscanf( fp, "%X set %m[^\n]\n", &id, &buf ) == 2 ) | |||
{ | |||
_loggables[ id ].unjournaled_state = new Log_Entry( buf ); | |||
free(buf); | |||
} | |||
fclose( fp ); | |||
return true; | |||
} | |||
#include <sys/stat.h> | |||
#include <unistd.h> | |||
/** replay journal or snapshot */ | |||
bool | |||
Loggable::replay ( const char *file ) | |||
{ | |||
if ( FILE *fp = fopen( file, "r" ) ) | |||
{ | |||
bool r = replay( fp ); | |||
fclose( fp ); | |||
return r; | |||
} | |||
else | |||
return false; | |||
} | |||
/** replay journal or snapshot */ | |||
bool | |||
Loggable::replay ( FILE *fp ) | |||
{ | |||
char *buf = NULL; | |||
struct stat st; | |||
fstat( fileno( fp ), &st ); | |||
off_t total = st.st_size; | |||
off_t current = 0; | |||
if ( _progress_callback ) | |||
_progress_callback( 0, _progress_callback_arg ); | |||
while ( fscanf( fp, "%m[^\n]\n", &buf ) == 1 ) | |||
{ | |||
if ( ! ( ! strcmp( buf, "{" ) || ! strcmp( buf, "}" ) ) ) | |||
{ | |||
if ( *buf == '\t' ) | |||
do_this( buf + 1, false ); | |||
else | |||
do_this( buf, false ); | |||
} | |||
free(buf); | |||
current = ftell( fp ); | |||
if ( _progress_callback ) | |||
_progress_callback( current * 100 / total, _progress_callback_arg ); | |||
} | |||
if ( _progress_callback ) | |||
_progress_callback( 0, _progress_callback_arg ); | |||
clear_dirty(); | |||
return true; | |||
} | |||
/** close journal and delete all loggable objects, returing the systemt to a blank slate */ | |||
bool | |||
Loggable::close ( void ) | |||
{ | |||
DMESSAGE( "closing journal and destroying all journaled objects" ); | |||
if ( _fp ) | |||
{ | |||
fclose( _fp ); | |||
_fp = NULL; | |||
} | |||
if ( ! snapshot( "snapshot" ) ) | |||
WARNING( "Failed to create snapshot" ); | |||
if ( ! save_unjournaled_state() ) | |||
WARNING( "Failed to save unjournaled state" ); | |||
for ( std::map <unsigned int, Loggable::log_pair >::iterator i = _loggables.begin(); | |||
i != _loggables.end(); ++i ) | |||
{ | |||
if ( i->second.loggable ) | |||
delete i->second.loggable; | |||
if ( i->second.unjournaled_state ) | |||
delete i->second.unjournaled_state; | |||
} | |||
_loggables.clear(); | |||
return true; | |||
} | |||
/** save out unjournaled state for all loggables */ | |||
bool | |||
Loggable::save_unjournaled_state ( void ) | |||
{ | |||
FILE *fp; | |||
fp = fopen( "unjournaled", "w" ); | |||
if ( ! fp ) | |||
{ | |||
DWARNING( "Could not open unjournaled state file for writing!" ); | |||
return false; | |||
} | |||
for ( std::map <unsigned int, Loggable::log_pair >::iterator i = _loggables.begin(); | |||
i != _loggables.end(); ++i ) | |||
{ | |||
/* get the latest state */ | |||
if ( i->second.loggable ) | |||
i->second.loggable->record_unjournaled(); | |||
if ( i->second.unjournaled_state ) | |||
{ | |||
char *s = i->second.unjournaled_state->print(); | |||
fprintf( fp, "0x%X set %s\n", i->first, s ); | |||
free( s ); | |||
} | |||
} | |||
fclose( fp ); | |||
return true; | |||
} | |||
/** must be called after construction in create() methods */ | |||
void | |||
Loggable::update_id ( unsigned int id ) | |||
{ | |||
/* make sure we're the last one */ | |||
ASSERT( _id == _log_id, "%u != %u", _id, _log_id ); | |||
assert( _loggables[ _id ].loggable == this ); | |||
_loggables[ _id ].loggable = NULL; | |||
_log_id = max( _log_id, id ); | |||
/* return this id number to the system */ | |||
// --_log_id; | |||
_id = id; | |||
if ( _loggables[ _id ].loggable ) | |||
FATAL( "Attempt to create object with an ID (0x%X) that already exists. The existing object is of type \"%s\", the new one is \"%s\". Corrupt journal?", _id, _loggables[ _id ].loggable->class_name(), class_name() ); | |||
_loggables[ _id ].loggable = this; | |||
} | |||
/** return a pointer to a static copy of /s/ with all special characters escaped */ | |||
const char * | |||
Loggable::escape ( const char *s ) | |||
{ | |||
static char r[512]; | |||
size_t i = 0; | |||
for ( ; *s && i < sizeof( r ); ++i, ++s ) | |||
{ | |||
if ( '\n' == *s ) | |||
{ | |||
r[ i++ ] = '\\'; | |||
r[ i ] = 'n'; | |||
} | |||
else if ( '"' == *s ) | |||
{ | |||
r[ i++ ] = '\\'; | |||
r[ i ] = '"'; | |||
} | |||
else | |||
r[ i ] = *s; | |||
} | |||
r[ i ] = '\0'; | |||
return r; | |||
} | |||
unsigned int Loggable::_relative_id = 0; | |||
/* calls to do_this() between invocation of this method and | |||
* end_relative_id_mode() will have all their IDs made relative to the | |||
* highest available ID at this time of this call. Non-Mixer uses | |||
* this to allow importing of module chains */ | |||
void | |||
Loggable::begin_relative_id_mode ( void ) | |||
{ | |||
_relative_id = ++_log_id; | |||
} | |||
void | |||
Loggable::end_relative_id_mode ( void ) | |||
{ | |||
_relative_id = 0; | |||
} | |||
/** 'do' a message like "Audio_Region 0xF1 set :r 123" */ | |||
bool | |||
Loggable::do_this ( const char *s, bool reverse ) | |||
{ | |||
unsigned int id = 0; | |||
char classname[40]; | |||
char command[40]; | |||
char *arguments = NULL; | |||
int found = sscanf( s, "%s %X %s ", classname, &id, command ); | |||
if ( 3 != found ) | |||
FATAL( "Invalid journal entry format \"%s\"", s ); | |||
const char *create, *destroy; | |||
if ( reverse ) | |||
{ | |||
// sscanf( s, "%s %*X %s %*[^\n<]<< %m[^\n]", classname, command, &arguments ); | |||
sscanf( s, "%s %*X %s%*[^\n<]<< %m[^\n]", classname, command, &arguments ); | |||
create = "destroy"; | |||
destroy = "create"; | |||
DMESSAGE( "undoing \"%s\"", s ); | |||
} | |||
else | |||
{ | |||
sscanf( s, "%s %*X %s %m[^\n<]", classname, command, &arguments ); | |||
create = "create"; | |||
destroy = "destroy"; | |||
} | |||
if ( ! strcmp( command, destroy ) ) | |||
{ | |||
Loggable *l = find( id ); | |||
/* deleting eg. a track, which contains a list of other | |||
widgets, causes destroy messages to be emitted for all those | |||
widgets, but when replaying the journal the destroy message | |||
causes the children to be deleted also... This is a temporary | |||
hack. Would it be better to queue up objects for deletion | |||
(when?) */ | |||
if ( l ) | |||
delete l; | |||
} | |||
else if ( ! strcmp( command, "set" ) ) | |||
{ | |||
// printf( "got set command (%s).\n", arguments ); | |||
Loggable *l = find( id ); | |||
ASSERT( l, "Unable to find object 0x%X referenced by command \"%s\"", id, s ); | |||
Log_Entry e( arguments ); | |||
l->log_start(); | |||
l->set( e ); | |||
l->log_end(); | |||
} | |||
else if ( ! strcmp( command, create ) ) | |||
{ | |||
Log_Entry e( arguments ); | |||
ASSERT( _class_map[ std::string( classname ) ], "Journal contains an object of class \"%s\", but I don't know how to create such objects.", classname ); | |||
{ | |||
if ( _relative_id ) | |||
id += _relative_id; | |||
/* create */ | |||
Loggable *l = _class_map[ std::string( classname ) ]( e, id ); | |||
l->log_create(); | |||
/* we're now creating a loggable. Apply any unjournaled | |||
* state it may have had in the past under this log ID */ | |||
Log_Entry *e = _loggables[ id ].unjournaled_state; | |||
if ( e ) | |||
l->set( *e ); | |||
} | |||
} | |||
if ( arguments ) | |||
free( arguments ); | |||
return true; | |||
} | |||
/** Reverse the last journal transaction */ | |||
void | |||
Loggable::undo ( void ) | |||
{ | |||
if ( ! _fp || /* journal not open */ | |||
1 == _undo_offset ) /* nothing left to undo */ | |||
return; | |||
char *buf; | |||
block_start(); | |||
long here = ftell( _fp ); | |||
fseek( _fp, _undo_offset, SEEK_SET ); | |||
if ( ( buf = backwards_afgets( _fp ) ) ) | |||
{ | |||
if ( ! strcmp( buf, "}\n" ) ) | |||
{ | |||
free( buf ); | |||
DMESSAGE( "undoing block" ); | |||
for ( ;; ) | |||
{ | |||
if ( ( buf = backwards_afgets( _fp ) ) ) | |||
{ | |||
char *s = buf; | |||
if ( *s != '\t' ) | |||
{ | |||
DMESSAGE( "done with block", s ); | |||
break; | |||
} | |||
else | |||
++s; | |||
do_this( s, true ); | |||
free( buf ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
do_this( buf, true ); | |||
free( buf ); | |||
} | |||
} | |||
off_t uo = ftell( _fp ); | |||
ASSERT( _undo_offset <= here, "WTF?" ); | |||
block_end(); | |||
_undo_offset = uo; | |||
} | |||
/** write a snapshot of the current state of all loggable objects to | |||
* file handle /fp/ */ | |||
bool | |||
Loggable::snapshot ( FILE *fp ) | |||
{ | |||
FILE *ofp = _fp; | |||
if ( ! Loggable::_snapshot_callback ) | |||
{ | |||
DWARNING( "No snapshot callback defined" ); | |||
return false; | |||
} | |||
if ( ! ( _fp = fp ) ) | |||
{ | |||
_fp = ofp; | |||
return false; | |||
} | |||
#ifndef NDEBUG | |||
_snapshotting = true; | |||
_snapshot_count++; | |||
#endif | |||
block_start(); | |||
Loggable::_snapshot_callback( _snapshot_callback_arg ); | |||
block_end(); | |||
#ifndef NDEBUG | |||
_snapshotting = false; | |||
#endif | |||
_fp = ofp; | |||
clear_dirty(); | |||
return true; | |||
} | |||
/** write a snapshot of the current state of all loggable objects to | |||
* file /name/ */ | |||
bool | |||
Loggable::snapshot ( const char *name ) | |||
{ | |||
FILE *fp; | |||
char *tmpname; | |||
asprintf( &tmpname, ".#%s", name ); | |||
if ( ! ( fp = fopen( tmpname, "w" ) )) | |||
return false; | |||
bool r = snapshot( fp ); | |||
fclose( fp ); | |||
rename( tmpname, name ); | |||
free(tmpname); | |||
return r; | |||
} | |||
/** Replace the journal with a snapshot of the current state */ | |||
void | |||
Loggable::compact ( void ) | |||
{ | |||
fseek( _fp, 0, SEEK_SET ); | |||
ftruncate( fileno( _fp ), 0 ); | |||
if ( ! snapshot( _fp ) ) | |||
FATAL( "Could not write snapshot!" ); | |||
fseek( _fp, 0, SEEK_END ); | |||
} | |||
#include <stdarg.h> | |||
/** Writes (part of) a line to the journal. Each separate line will be | |||
* stored separately in _transaction until transaction is closed. | |||
*/ | |||
void | |||
Loggable::log ( const char *fmt, ... ) | |||
{ | |||
Locker lock( _lock ); | |||
static char * buf = NULL; | |||
static size_t i = 0; | |||
static size_t buf_size = 0; | |||
if ( ! _fp ) | |||
return; | |||
if ( NULL == buf ) | |||
{ | |||
buf_size = 1024; | |||
buf = (char*)malloc( buf_size ); | |||
} | |||
va_list args; | |||
if ( fmt ) | |||
{ | |||
va_start( args, fmt ); | |||
for ( ;; ) | |||
{ | |||
size_t l = vsnprintf( buf + i, buf_size - i, fmt, args ); | |||
if ( l >= buf_size - i ) | |||
{ | |||
buf = (char*)realloc( buf, buf_size += (l + 1) + buf_size ); | |||
} | |||
else | |||
{ | |||
i += l; | |||
break; | |||
} | |||
} | |||
va_end( args ); | |||
} | |||
if ( '\n' == buf[i-1] ) | |||
{ | |||
_transaction.push( strdup( buf ) ); | |||
i = 0; | |||
} | |||
} | |||
/** End the current transaction and commit it to the journal */ | |||
void | |||
Loggable::flush ( void ) | |||
{ | |||
if ( ! _fp ) | |||
{ | |||
// printf( "error: no log file open!\n" ); | |||
while ( ! _transaction.empty() ) | |||
{ | |||
free( _transaction.front() ); | |||
_transaction.pop(); | |||
} | |||
return; | |||
} | |||
int n = _transaction.size(); | |||
if ( n > 1 ) | |||
fprintf( _fp, "{\n" ); | |||
while ( ! _transaction.empty() ) | |||
{ | |||
char *s = _transaction.front(); | |||
_transaction.pop(); | |||
if ( n > 1 ) | |||
fprintf( _fp, "\t" ); | |||
fprintf( _fp, "%s", s ); | |||
free( s ); | |||
} | |||
if ( n > 1 ) | |||
fprintf( _fp, "}\n" ); | |||
if ( n ) | |||
/* something done, reset undo index */ | |||
_undo_offset = ftell( _fp ); | |||
fflush( _fp ); | |||
} | |||
/** Print bidirectional journal entry */ | |||
void | |||
Loggable::log_print( const Log_Entry *o, const Log_Entry *n ) const | |||
{ | |||
if ( ! _fp ) | |||
return; | |||
if ( n ) | |||
for ( int i = 0; i < n->size(); ++i ) | |||
{ | |||
const char *s, *v; | |||
n->get( i, &s, &v ); | |||
log( "%s %s%s", s, v, n->size() == i + 1 ? "" : " " ); | |||
} | |||
if ( o && o->size() ) | |||
{ | |||
if ( n ) log( " << " ); | |||
for ( int i = 0; i < o->size(); ++i ) | |||
{ | |||
const char *s, *v; | |||
o->get( i, &s, &v ); | |||
log( "%s %s%s", s, v, o->size() == i + 1 ? "" : " " ); | |||
} | |||
} | |||
log( "\n" ); | |||
} | |||
/** Remember current object state for later comparison. *Must* be | |||
* called before any user action that might change one of the object's | |||
* journaled properties. */ | |||
void | |||
Loggable::log_start ( void ) | |||
{ | |||
Locker lock( _lock );; | |||
if ( ! _old_state ) | |||
{ | |||
_old_state = new Log_Entry; | |||
get( *_old_state ); | |||
} | |||
++_nest; | |||
} | |||
/** Log any change to the object's state since log_start(). */ | |||
void | |||
Loggable::log_end ( void ) | |||
{ | |||
Locker lock( _lock );; | |||
ASSERT( _old_state, "Programming error: log_end() called before log_start()" ); | |||
if ( --_nest > 0 ) | |||
return; | |||
Log_Entry *new_state; | |||
new_state = new Log_Entry; | |||
get( *new_state ); | |||
if ( Log_Entry::diff( _old_state, new_state ) ) | |||
{ | |||
log( "%s 0x%X set ", class_name(), _id ); | |||
log_print( _old_state, new_state ); | |||
set_dirty(); | |||
} | |||
delete new_state; | |||
delete _old_state; | |||
_old_state = NULL; | |||
if ( Loggable::_level == 0 ) | |||
Loggable::flush(); | |||
} | |||
/** Log object creation. *Must* be called at the end of all public | |||
* constructors for leaf classes */ | |||
void | |||
Loggable::log_create ( void ) const | |||
{ | |||
Locker lock( _lock );; | |||
set_dirty(); | |||
if ( ! _fp ) | |||
/* replaying, don't bother */ | |||
return; | |||
#ifndef NDEBUG | |||
if ( _snapshotting && _snapshot_count != _num_snapshot ) | |||
{ | |||
_num_snapshot_creates = 1; | |||
_num_snapshot = _snapshot_count; | |||
} | |||
else if ( _snapshotting && _snapshot_count == _num_snapshot ) | |||
{ | |||
_num_snapshot_creates++; | |||
ASSERT( _num_snapshot_creates < 2, "Attempt to log creation of same object twice in one snapshot! %s", class_name() ); | |||
} | |||
else | |||
{ | |||
_num_log_creates++; | |||
ASSERT( _num_log_creates < 2, "Attempt to log creation of same object twice in the journal! %s", class_name() ); | |||
} | |||
#endif | |||
log( "%s 0x%X create ", class_name(), _id ); | |||
Log_Entry e; | |||
get( e ); | |||
if ( e.size() ) | |||
log_print( NULL, &e ); | |||
else | |||
log( "\n" ); | |||
if ( Loggable::_level == 0 ) | |||
Loggable::flush(); | |||
} | |||
/** record this loggable's unjournaled state in memory */ | |||
void | |||
Loggable::record_unjournaled ( void ) const | |||
{ | |||
Log_Entry *e = new Log_Entry(); | |||
get_unjournaled( *e ); | |||
Log_Entry **le = &_loggables[ _id ].unjournaled_state; | |||
if ( *le ) | |||
{ | |||
delete *le; | |||
*le = NULL; | |||
} | |||
if ( e->size() ) | |||
*le = e; | |||
else | |||
delete e; | |||
} | |||
/** Log object destruction. *Must* be called at the beginning of the | |||
* destructors of leaf classes */ | |||
void | |||
Loggable::log_destroy ( void ) const | |||
{ | |||
Locker lock( _lock );; | |||
set_dirty(); | |||
if ( ! _fp ) | |||
/* tearing down... don't bother */ | |||
return; | |||
/* the unjournaled state may have changed: make a note of it. */ | |||
record_unjournaled(); | |||
log( "%s 0x%X destroy << ", class_name(), _id ); | |||
Log_Entry e; | |||
get( e ); | |||
log_print( NULL, &e ); | |||
if ( Loggable::_level == 0 ) | |||
Loggable::flush(); | |||
} |
@@ -1,280 +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. */ | |||
/*******************************************************************************/ | |||
/* Master class for journaling. */ | |||
#pragma once | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <assert.h> | |||
#include <map> | |||
#include <string> | |||
#include <queue> | |||
// #include "types.h" | |||
typedef void (progress_func)( int, void * ); | |||
typedef void (snapshot_func)( void * ); | |||
typedef void (dirty_func)( int, void * ); | |||
class Log_Entry; | |||
class Loggable; | |||
typedef Loggable *(create_func)(Log_Entry &, unsigned int id); | |||
#define LOG_REGISTER_CREATE( class ) \ | |||
Loggable::register_create( #class, & class ::create ) | |||
#define LOG_NAME_FUNC( class ) \ | |||
virtual const char *class_name ( void ) const { return #class ; } | |||
#define LOG_CREATE_FUNC( class ) \ | |||
static Loggable * \ | |||
create ( Log_Entry &e, unsigned int id ) \ | |||
{ \ | |||
class *r = new class; \ | |||
r->update_id( id ); \ | |||
r->set( e ); \ | |||
return (Loggable *)r; \ | |||
} \ | |||
LOG_NAME_FUNC( class ) | |||
#define LOG_NOT_LOGGABLE_FUNC( class ) \ | |||
virtual const char *class_name ( void ) const { return #class ; } | |||
class Logger; | |||
class Loggable | |||
{ | |||
struct log_pair { | |||
Loggable * loggable; | |||
Log_Entry * unjournaled_state; | |||
}; | |||
static bool _readonly; | |||
static FILE *_fp; | |||
static unsigned int _log_id; | |||
static int _level; | |||
static off_t _undo_offset; | |||
static std::map <unsigned int, Loggable::log_pair > _loggables; | |||
static std::map <std::string, create_func*> _class_map; | |||
static std::queue <char *> _transaction; | |||
static progress_func *_progress_callback; | |||
static void *_progress_callback_arg; | |||
static snapshot_func *_snapshot_callback; | |||
static void *_snapshot_callback_arg; | |||
static dirty_func *_dirty_callback; | |||
static void *_dirty_callback_arg; | |||
private: | |||
static unsigned int _relative_id; | |||
#ifndef NDEBUG | |||
static bool _snapshotting; | |||
static int _snapshot_count; | |||
mutable int _num_log_creates; | |||
mutable int _num_snapshot; | |||
mutable int _num_snapshot_creates; | |||
#endif | |||
unsigned int _id; | |||
Log_Entry *_old_state; | |||
int _nest; | |||
static int _dirty; /* count of changes */ | |||
static void ensure_size ( size_t n ); | |||
void log_print ( const Log_Entry *o, const Log_Entry *n ) const; | |||
static void log ( const char *fmt, ... ); | |||
static void flush ( void ); | |||
void init ( bool loggable=true ) | |||
{ | |||
// _new_state | |||
#ifndef NDEBUG | |||
_num_log_creates = 0; | |||
_num_snapshot = 0; | |||
_num_snapshot_creates = 0; | |||
#endif | |||
_old_state = NULL; | |||
_nest = 0; | |||
if ( loggable ) | |||
{ | |||
_id = ++_log_id; | |||
_loggables[ _id ].loggable = this; | |||
} | |||
else | |||
_id = 0; | |||
} | |||
/* not implemented */ | |||
const Loggable & operator= ( const Loggable &rhs ); | |||
void record_unjournaled ( void ) const; | |||
static bool load_unjournaled_state ( void ); | |||
static bool replay ( FILE *fp ); | |||
static void signal_dirty ( int v ) { if ( _dirty_callback ) _dirty_callback( v, _dirty_callback_arg ); } | |||
static void set_dirty ( void ) { signal_dirty( ++_dirty ); } | |||
static void clear_dirty ( void ) { signal_dirty( _dirty = 0 ); } | |||
public: | |||
static bool readonly ( void ) { return _readonly; } | |||
static bool replay ( const char *name ); | |||
static bool snapshot( FILE * fp ); | |||
static bool snapshot( const char *name ); | |||
static void snapshot_callback ( snapshot_func *p, void *arg ) { _snapshot_callback = p; _snapshot_callback_arg = arg; } | |||
static void progress_callback ( progress_func *p, void *arg ) { _progress_callback = p; _progress_callback_arg = arg;} | |||
static void dirty_callback ( dirty_func *p, void *arg ) { _dirty_callback = p; _dirty_callback_arg = arg;} | |||
static const char *escape ( const char *s ); | |||
unsigned int id ( void ) const { return _id; } | |||
static bool save_unjournaled_state ( void ); | |||
static bool open ( const char *filename ); | |||
static bool close ( void ); | |||
static void undo ( void ); | |||
static void compact ( void ); | |||
static void block_start ( void ); | |||
static void block_end ( void ); | |||
static Loggable * find ( unsigned int id ); | |||
Loggable ( bool loggable=true ) | |||
{ | |||
init( loggable ); | |||
} | |||
void update_id ( unsigned int id ); | |||
virtual ~Loggable ( ); | |||
static | |||
void | |||
register_create ( const char *name, create_func *func ) | |||
{ | |||
_class_map[ std::string( name ) ] = func; | |||
} | |||
/* log messages for journal */ | |||
virtual void get ( Log_Entry &e ) const = 0; | |||
virtual void get_unjournaled ( Log_Entry & ) const | |||
{ | |||
/* implementation optional */ | |||
} | |||
virtual void set ( Log_Entry &e ) = 0; | |||
virtual const char *class_name ( void ) const = 0; | |||
virtual void log_children ( void ) const { return; } | |||
static void begin_relative_id_mode ( void ); | |||
static void end_relative_id_mode ( void ); | |||
static bool do_this ( const char *s, bool reverse ); | |||
static int dirty ( void ) { return _dirty; } | |||
void log_create ( void ) const; | |||
protected: | |||
void log_start ( void ); | |||
void log_end ( void ); | |||
void log_destroy ( void ) const; | |||
/* leaf subclasses *must* call log_create() at the end of their copy contructors */ | |||
Loggable ( const Loggable & ) | |||
{ | |||
init( true ); | |||
} | |||
public: | |||
friend class Logger; | |||
}; | |||
class Logger | |||
{ | |||
Loggable *_this; | |||
Logger ( ) {} | |||
/* not permitted */ | |||
Logger ( const Logger &rhs ); | |||
const Logger & operator= ( const Logger &rhs ); | |||
public: | |||
Logger ( Loggable *l ) : _this( l ) | |||
{ | |||
_this->log_start(); | |||
} | |||
~Logger ( ) | |||
{ | |||
_this->log_end(); | |||
} | |||
void hold ( void ) | |||
{ | |||
_this->_nest++; | |||
} | |||
void release ( void ) | |||
{ | |||
_this->_nest--; | |||
assert( _this->_nest ); | |||
} | |||
}; | |||
#include "Log_Entry.H" |
@@ -1,164 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* Copyright (C) 2007-2008 Jonathan Moore Liles */ | |||
/* */ | |||
/* This program is free software; you can redistribute it and/or modify it */ | |||
/* under the terms of the GNU General Public License as published by the */ | |||
/* Free Software Foundation; either version 2 of the License, or (at your */ | |||
/* option) any later version. */ | |||
/* */ | |||
/* This program is distributed in the hope that it will be useful, but WITHOUT */ | |||
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ | |||
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ | |||
/* more details. */ | |||
/* */ | |||
/* You should have received a copy of the GNU General Public License along */ | |||
/* with This program; see the file COPYING. If not,write to the Free Software */ | |||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||
/*******************************************************************************/ | |||
#include "event.H" | |||
#include <stdio.h> | |||
#include <string.h> | |||
namespace MIDI | |||
{ | |||
void | |||
event::_init ( void ) | |||
{ | |||
_link = _next = _prev = NULL; | |||
_selected = 0; | |||
} | |||
event::event ( void ) | |||
{ | |||
_init(); | |||
} | |||
event::~event ( void ) | |||
{ | |||
_link = _next = _prev = NULL; | |||
} | |||
/* copy constructor */ | |||
event::event ( const event &e ) : midievent( e ) | |||
{ | |||
_link = _next = _prev = NULL; | |||
_selected = e._selected; | |||
} | |||
event::event ( const midievent &e ) : midievent( e ) | |||
{ | |||
_init(); | |||
} | |||
void | |||
event::link ( event *event ) | |||
{ | |||
if ( event == NULL ) | |||
{ | |||
if ( _link ) | |||
{ | |||
_link->_link = NULL; | |||
_link = NULL; | |||
} | |||
return; | |||
} | |||
_link = event; | |||
_link->_link = this; | |||
} | |||
event * | |||
event::link ( void ) const | |||
{ | |||
return _link; | |||
} | |||
bool | |||
event::linked ( void ) const | |||
{ | |||
return _link != NULL; | |||
} | |||
void | |||
event::select ( void ) | |||
{ | |||
_selected = 1; | |||
if ( _link ) | |||
_link->_selected = 1; | |||
} | |||
void | |||
event::deselect ( void ) | |||
{ | |||
_selected = 0; | |||
if ( _link ) | |||
_link->_selected = 0; | |||
} | |||
bool | |||
event::selected ( int n ) const | |||
{ | |||
return _selected == n; | |||
} | |||
bool | |||
event::selected ( void ) const | |||
{ | |||
return _selected == 1; | |||
} | |||
/* override this so we can update linked event */ | |||
void | |||
event::note ( char note ) | |||
{ | |||
midievent::note( note ); | |||
if ( _link ) | |||
_link->midievent::note( note ); | |||
} | |||
/* stupid C++ makes us override the all polymorphic functions... */ | |||
unsigned char | |||
event::note ( void ) const | |||
{ | |||
return midievent::note(); | |||
} | |||
tick_t | |||
event::note_duration ( void ) const | |||
{ | |||
return _link ? _link->timestamp() - timestamp() : 0; | |||
} | |||
void | |||
event::note_duration ( tick_t l ) | |||
{ | |||
if ( _link ) | |||
_link->timestamp( timestamp() + l ); | |||
} | |||
void | |||
event::get_note_properties ( note_properties *p ) const | |||
{ | |||
p->start = timestamp(); | |||
p->duration = note_duration(); | |||
p->velocity = note_velocity(); | |||
p->note = note(); | |||
p->selected = selected(); | |||
} | |||
void | |||
event::set_note_properties ( const note_properties *p ) | |||
{ | |||
timestamp( p->start ); | |||
note_duration( p->duration ); | |||
note_velocity( p->velocity ); | |||
note( p->note ); | |||
selected( p->selected ); | |||
} | |||
} |
@@ -1,100 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* Copyright (C) 2007-2008 Jonathan Moore Liles */ | |||
/* */ | |||
/* This program is free software; you can redistribute it and/or modify it */ | |||
/* under the terms of the GNU General Public License as published by the */ | |||
/* Free Software Foundation; either version 2 of the License, or (at your */ | |||
/* option) any later version. */ | |||
/* */ | |||
/* This program is distributed in the hope that it will be useful, but WITHOUT */ | |||
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ | |||
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ | |||
/* more details. */ | |||
/* */ | |||
/* You should have received a copy of the GNU General Public License along */ | |||
/* with This program; see the file COPYING. If not,write to the Free Software */ | |||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||
/*******************************************************************************/ | |||
/* Higher level event interface than midievent, supporting | |||
doublely-linked list, marking, selection, and linking of note | |||
on/off pairs. */ | |||
#pragma once | |||
#include "midievent.H" | |||
namespace MIDI | |||
{ | |||
class event_list; | |||
class event; | |||
class note_properties { | |||
public: | |||
tick_t start; | |||
tick_t duration; | |||
int note; | |||
int velocity; | |||
bool selected; | |||
}; | |||
class event : public midievent | |||
{ | |||
protected: | |||
/* these are only to be used by event_list class! */ | |||
event *_next; | |||
event *_prev; | |||
private: | |||
event *_link; /* other event in pair */ | |||
byte_t _selected; | |||
void _init ( void ); | |||
public: | |||
event(); | |||
~event(); | |||
event ( const event &e ); | |||
event ( const midievent &e ); | |||
event * next ( void ) const; | |||
event * prev ( void ) const; | |||
void link ( event *event ); | |||
event * link ( void ) const; | |||
bool linked ( void ) const; | |||
void select ( void ); | |||
void deselect ( void ); | |||
bool selected ( int n ) const; | |||
bool selected ( void ) const; | |||
virtual void note ( char note ); | |||
virtual unsigned char note ( void ) const; | |||
tick_t note_duration ( void ) const; | |||
void note_duration ( tick_t l ); | |||
void get_note_properties ( note_properties *e ) const; | |||
void set_note_properties ( const note_properties *e ); | |||
friend class event_list; | |||
}; | |||
inline event * | |||
event::next ( void ) const | |||
{ | |||
return _next; | |||
} | |||
inline event * | |||
event::prev ( void ) const | |||
{ | |||
return _prev; | |||
} | |||
} |
@@ -1,721 +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 "debug.h" | |||
#include "event_list.H" | |||
/* The operations we perform on event lists are clumsy with STL lists | |||
and iterators so we have a custom doubly-linked list implementation | |||
here for complete control */ | |||
namespace MIDI | |||
{ | |||
#define RFOR_ALL( it ) for ( event *next, * it = _tail; it && ((next = it ->_prev), true) ; it = next ) | |||
#define FOR_ALL( it ) for ( event *next, * it = _head; it && ((next = it ->_next), true) ; it = next ) | |||
// #define FOR_ALL( e ) for ( event * e = _head; e; e = e ->_next ) | |||
#define FOR_SELECTED( e ) FOR_ALL( e ) if ( e ->selected() ) | |||
#define RFOR_SELECTED( e ) RFOR_ALL( e ) if ( e ->selected() ) | |||
event_list::event_list ( void ) | |||
{ | |||
_head = NULL; | |||
_tail = NULL; | |||
_size = 0; | |||
} | |||
event_list::~event_list ( void ) | |||
{ | |||
clear(); | |||
} | |||
/* copy constructor */ | |||
event_list::event_list ( const event_list &el ) | |||
{ | |||
_copy( &el ); | |||
} | |||
event_list & | |||
event_list::operator= ( const event_list &rhs ) | |||
{ | |||
if ( this != &rhs ) | |||
{ | |||
clear(); | |||
_copy( &rhs ); | |||
} | |||
return *this; | |||
} | |||
event_list & | |||
event_list::operator= ( const list <midievent> &rhs ) | |||
{ | |||
clear(); | |||
for ( list <midievent>::const_iterator me = rhs.begin(); me != rhs.end(); me++ ) | |||
{ | |||
event *e = new event( *me ); | |||
_insert( NULL, e ); | |||
} | |||
relink(); | |||
return *this; | |||
} | |||
/** allow indexing */ | |||
event * | |||
event_list::operator[] ( unsigned int index ) | |||
{ | |||
unsigned int i = 0; | |||
for ( event *e = _head; e; (e = e->_next), ++i ) | |||
if ( i == index ) | |||
return e; | |||
// all else fails. | |||
return _tail; | |||
} | |||
void | |||
event_list::_copy ( const event_list *el ) | |||
{ | |||
if ( ! el->_head ) | |||
{ | |||
_head = _tail = NULL; | |||
_size = 0; | |||
return; | |||
} | |||
_head = new event( *(el->_head) ); | |||
_head->_prev = NULL; | |||
event *p = _head; | |||
for ( event *e = el->_head->_next; e; e = e->_next ) | |||
{ | |||
event *n = new event( *e ); | |||
n->_next = NULL; | |||
p->_next = n; | |||
n->_prev = p; | |||
p = n; | |||
} | |||
_tail = p; | |||
_size = el->_size; | |||
relink(); | |||
} | |||
/** insert event /n/ before event /o/ */ | |||
void | |||
event_list::_insert ( event *o, event *n ) | |||
{ | |||
++_size; | |||
if ( ! o ) | |||
{ | |||
n->_next = NULL; | |||
n->_prev = _tail; | |||
if ( _tail ) | |||
_tail->_next = n; | |||
_tail = n; | |||
if ( ! _head ) | |||
_head = n; | |||
return; | |||
} | |||
event *t = o->_prev; | |||
o->_prev = n; | |||
n->_next = o; | |||
n->_prev = t; | |||
if ( ! t ) | |||
_head = n; | |||
else | |||
t->_next = n; | |||
} | |||
void | |||
event_list::unlink ( event *e ) | |||
{ | |||
if ( e->_next ) | |||
e->_next->_prev = e->_prev; | |||
else | |||
_tail = e->_prev; | |||
if ( e->_prev ) | |||
e->_prev->_next = e->_next; | |||
else | |||
_head = e->_next; | |||
--_size; | |||
} | |||
void | |||
event_list::clear ( void ) | |||
{ | |||
for ( event *e = _head; e ; ) | |||
{ | |||
event *n = e->_next; | |||
delete e; | |||
e = n; | |||
} | |||
_head = NULL; | |||
_tail = NULL; | |||
_size = 0; | |||
} | |||
void | |||
event_list::mix ( event *ne ) | |||
{ | |||
FOR_ALL( e ) | |||
if ( *e == *ne ) | |||
{ | |||
/* already have an event like this, drop it */ | |||
if ( ne->linked() ) | |||
delete ne->link(); | |||
delete ne; | |||
return; | |||
} | |||
insert( ne ); | |||
if ( ne->linked() ) | |||
insert( ne->link() ); | |||
} | |||
/** remove elements from list /el/ to this list */ | |||
void | |||
event_list::merge ( event_list *el ) | |||
{ | |||
event *n; | |||
for ( event *e = el->_head; e; e = n ) | |||
{ | |||
n = e->_next; | |||
el->unlink( e ); | |||
insert( e ); | |||
} | |||
} | |||
/** unlink event e */ | |||
void | |||
event_list::remove ( event *e ) | |||
{ | |||
unlink( e ); | |||
delete e; | |||
} | |||
/** sorted insert /e/ */ | |||
void | |||
event_list::insert ( event *e ) | |||
{ | |||
/* find the place to insert */ | |||
RFOR_ALL( i ) | |||
if ( *e >= *i ) | |||
{ | |||
_insert( i->_next, e ); | |||
return; | |||
} | |||
_insert( _head, e ); | |||
} | |||
/** just append event without sorting */ | |||
void | |||
event_list::append ( event *e ) | |||
{ | |||
_insert( NULL, e ); | |||
} | |||
event * | |||
event_list::first ( void ) const | |||
{ | |||
return _head; | |||
} | |||
event * | |||
event_list::last ( void ) const | |||
{ | |||
return _tail; | |||
} | |||
/*************/ | |||
/* Selection */ | |||
/*************/ | |||
/** select all events from /start/ to /end/ inclusive */ | |||
void | |||
event_list::select ( tick_t start, tick_t end ) | |||
{ | |||
FOR_ALL( e ) | |||
{ | |||
tick_t ts = e->timestamp(); | |||
/* don't count note offs exactly on start */ | |||
if ( ts == start && e->is_note_off() ) | |||
continue; | |||
if ( ts >= start && ts < end ) | |||
e->select(); | |||
} | |||
} | |||
/** select note evenets from /start/ to /end/ within range /hi/ through /lo/ */ | |||
void | |||
event_list::select ( tick_t start, tick_t end, int hi, int lo ) | |||
{ | |||
FOR_ALL( e ) | |||
{ | |||
tick_t ts = e->timestamp(); | |||
/* don't count note offs exactly on start */ | |||
if ( ! e->is_note_on() ) | |||
continue; | |||
if ( ts >= start && ts < end && | |||
e->note() <= hi && e->note() >= lo ) | |||
e->select(); | |||
} | |||
} | |||
/** select ALL events */ | |||
void | |||
event_list::select_all ( void ) | |||
{ | |||
FOR_ALL( e ) | |||
e->select(); | |||
} | |||
void | |||
event_list::select_none ( void ) | |||
{ | |||
FOR_ALL( e ) | |||
e->deselect(); | |||
} | |||
void | |||
event_list::invert_selection ( void ) | |||
{ | |||
FOR_ALL( e ) | |||
if ( ! e->is_note_off() ) | |||
{ | |||
if ( e->selected() ) | |||
e->deselect(); | |||
else | |||
e->select(); | |||
} | |||
} | |||
/** remove all selected events */ | |||
void | |||
event_list::remove_selected ( void ) | |||
{ | |||
FOR_SELECTED( e ) | |||
{ | |||
remove( e ); | |||
} | |||
} | |||
/** copy selected events into event list /el/ */ | |||
void | |||
event_list::copy_selected ( event_list *el ) const | |||
{ | |||
event *fi = first(); | |||
if ( ! fi ) | |||
return; | |||
tick_t offset = fi->timestamp(); | |||
FOR_SELECTED( e ) | |||
{ | |||
event *nel = 0; | |||
if ( e->linked() ) | |||
nel = new event(*e->link()); | |||
event *ne = new event(*e); | |||
ne->timestamp( ne->timestamp() - offset ); | |||
if ( nel ) | |||
{ | |||
nel->link( ne ); | |||
ne->link( nel ); | |||
nel->timestamp( nel->timestamp() - offset ); | |||
} | |||
el->mix(ne); | |||
} | |||
} | |||
/** add events from list /el/ */ | |||
void | |||
event_list::paste ( tick_t offset, const event_list *el ) | |||
{ | |||
event *n; | |||
for ( event *e = el->_head; e; e = n ) | |||
{ | |||
n = e->_next; | |||
event *ne = new event(*e); | |||
ne->link( NULL ); | |||
ne->timestamp( ne->timestamp() + offset ); | |||
insert( ne ); | |||
} | |||
relink(); | |||
} | |||
/** transpose selected notes (ignoring other event types) by /n/ tones | |||
* (may span octaves) */ | |||
void | |||
event_list::transpose_selected ( int n ) | |||
{ | |||
FOR_SELECTED( e ) | |||
{ | |||
if ( e->is_note_on() ) | |||
e->note( e->note() + n ); | |||
} | |||
} | |||
/** change all notes of value /from/ to /to/ */ | |||
void | |||
event_list::rewrite_selected ( int from, int to ) | |||
{ | |||
FOR_SELECTED( e ) | |||
{ | |||
if ( e->is_note_on() && e->note() == from ) | |||
e->note( to ); | |||
} | |||
} | |||
/** set velocity of selected notes to /v/ */ | |||
void | |||
event_list::selected_velocity ( int v ) | |||
{ | |||
FOR_SELECTED( e ) | |||
{ | |||
if ( e->is_note_on() ) | |||
e->note_velocity( v ); | |||
} | |||
} | |||
/** get timestamp of earliest selected event */ | |||
tick_t | |||
event_list::selection_min ( void ) const | |||
{ | |||
FOR_SELECTED( e ) | |||
return e->timestamp(); | |||
return 0; | |||
} | |||
tick_t | |||
event_list::selection_max ( void ) const | |||
{ | |||
RFOR_SELECTED( e ) | |||
return e->timestamp(); | |||
return 0; | |||
} | |||
/** move selected events by offset /o/ */ | |||
void | |||
event_list::nudge_selected ( long o ) | |||
{ | |||
if ( o < 0 ) | |||
if ( selection_min() < (tick_t)( 0 - o ) ) | |||
return; | |||
if ( o < 0 ) | |||
{ | |||
FOR_SELECTED( e ) | |||
move( e, o ); | |||
} | |||
else | |||
{ | |||
RFOR_SELECTED( e ) | |||
move( e, o ); | |||
} | |||
} | |||
/** move block of selected events to tick /tick/ */ | |||
void | |||
event_list::move_selected ( tick_t tick ) | |||
{ | |||
/* if ( o < 0 ) */ | |||
/* if ( selection_min() < (tick_t)( 0 - o ) ) */ | |||
/* return; */ | |||
tick_t min = selection_min(); | |||
tick_t offset = tick - min; | |||
nudge_selected( offset ); | |||
/* FOR_SELECTED( e ) */ | |||
/* { */ | |||
/* e->timestamp( e->timestamp() + offset ); */ | |||
/* sort( e ); */ | |||
/* move( e, o ); */ | |||
/* } */ | |||
} | |||
void | |||
event_list::push_selection ( void ) | |||
{ | |||
FOR_ALL( e ) | |||
if ( e->_selected ) | |||
++e->_selected; | |||
} | |||
void | |||
event_list::pop_selection ( void ) | |||
{ | |||
FOR_ALL( e ) | |||
if ( e->_selected ) | |||
--e->_selected; | |||
} | |||
/** verify that all note ons are linked to note offs */ | |||
bool | |||
event_list::verify ( void ) const | |||
{ | |||
FOR_ALL( e ) | |||
if ( e->is_note_on() && ! e->linked() ) | |||
return false; | |||
return true; | |||
} | |||
/** link /e/ (a note on) with the next corresponding note off */ | |||
void | |||
event_list::link ( event *on ) | |||
{ | |||
if ( ! on->is_note_on() ) | |||
return; | |||
for ( event *off = on->_next; off; off = off->_next ) | |||
{ | |||
if ( off->linked() ) | |||
continue; | |||
if ( off->is_note_off() && | |||
off->channel() == on->channel() && | |||
off->note() == on->note() ) | |||
{ | |||
on->link( off ); | |||
return; | |||
} | |||
} | |||
WARNING( "no corresponding note_off found for note on, repairing" ); | |||
event *off = new event( *on ); | |||
off->opcode( event::NOTE_OFF ); | |||
on->link( off ); | |||
insert( off ); | |||
} | |||
/** insert /l/ ticks of time at /start/ */ | |||
void | |||
event_list::insert_time ( tick_t start, tick_t l ) | |||
{ | |||
FOR_ALL( e ) | |||
{ | |||
tick_t ts = e->timestamp(); | |||
if ( e->is_note_off() ) | |||
continue; | |||
if ( ts >= start ) | |||
{ | |||
if ( e->is_note_on() ) | |||
{ | |||
/* only notes ENTIRELY WITHIN the range will be moved */ | |||
e->timestamp( ts + l ); | |||
e->link()->timestamp( e->link()->timestamp() + l ); | |||
} | |||
else | |||
e->timestamp( e->timestamp() + l ); | |||
} | |||
} | |||
sort(); | |||
} | |||
/** delete events in range and close the gap */ | |||
void | |||
event_list::delete_time ( tick_t start, tick_t end ) | |||
{ | |||
tick_t l = end - start; | |||
push_selection(); | |||
select( start, end ); | |||
remove_selected(); | |||
pop_selection(); | |||
/* cut out the slack */ | |||
FOR_ALL( e ) | |||
{ | |||
tick_t ts = e->timestamp(); | |||
if ( ts >= end ) | |||
e->timestamp( ts - l ); | |||
} | |||
} | |||
/** link all note ons to subsequent note offs */ | |||
void | |||
event_list::relink ( void ) | |||
{ | |||
/* clear links */ | |||
FOR_ALL( e ) | |||
e->link( NULL ); | |||
/* link */ | |||
FOR_ALL( on ) | |||
link( on ); | |||
if ( ! verify() ) | |||
FATAL( "event list failed verification" ); | |||
} | |||
/** resort event /e/ */ | |||
void | |||
event_list::sort ( event *e ) | |||
{ | |||
unlink( e ); | |||
insert( e ); | |||
} | |||
/** resort entire list */ | |||
void | |||
event_list::sort ( void ) | |||
{ | |||
event_list *temp = new event_list( ); | |||
_head = temp->_head; | |||
_tail = temp->_tail; | |||
FOR_ALL( n ) | |||
temp->insert( n ); | |||
temp->_head = NULL; | |||
delete temp; | |||
relink(); | |||
} | |||
/** move event /e/ by /o/ ticks */ | |||
void | |||
event_list::move ( event *e, long o ) | |||
{ | |||
e->timestamp( e->timestamp() + o ); | |||
sort( e ); | |||
} | |||
bool | |||
event_list::empty ( void ) const | |||
{ | |||
return _head == NULL; | |||
} | |||
size_t | |||
event_list::size ( void ) const | |||
{ | |||
return _size; | |||
} | |||
void | |||
event_list::_hi_lo ( bool sel, int *hi, int *lo ) const | |||
{ | |||
*hi = 0; | |||
*lo = 127; | |||
FOR_ALL( e ) | |||
{ | |||
if ( sel && ! e->selected() ) | |||
continue; | |||
if ( ! e->is_note_on() ) | |||
continue; | |||
int n = e->note(); | |||
if ( n > *hi ) | |||
*hi = n; | |||
if ( n < *lo ) | |||
*lo = n; | |||
} | |||
} | |||
/** set /hi/ and /lo/ to the lowest and highest pitched note events in | |||
* this list, respectively */ | |||
void | |||
event_list::hi_lo_note ( int *hi, int *lo ) const | |||
{ | |||
_hi_lo( false, hi, lo ); | |||
} | |||
void | |||
event_list::selected_hi_lo_note ( int *hi, int *lo ) const | |||
{ | |||
_hi_lo( true, hi, lo ); | |||
} | |||
} |
@@ -1,94 +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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include "event.H" | |||
#include <list> | |||
namespace MIDI { | |||
using std::list; | |||
class midievent; | |||
class event_list { | |||
event * _head; | |||
event * _tail; | |||
size_t _size; | |||
void _insert ( event *o, event *n ); | |||
void _copy ( const event_list *el ); | |||
void _hi_lo ( bool sel, int *hi, int *lo ) const; | |||
public: | |||
event_list ( void ); | |||
~event_list ( void ); | |||
event_list ( const event_list &el ); | |||
void clear ( void ); | |||
void merge ( event_list *el ); | |||
void unlink ( event *e ); | |||
void remove ( event *e ); | |||
void insert ( event *e ); | |||
event * first ( void ) const; | |||
event * last ( void ) const; | |||
void select ( tick_t start, tick_t end ); | |||
void select ( tick_t start, tick_t end, int hi, int lo ); | |||
void select_all ( void ); | |||
void select_none ( void ); | |||
void invert_selection ( void ); | |||
void copy_selected ( event_list *el ) const; | |||
void paste ( tick_t offset, const event_list *el ); | |||
void remove_selected ( void ); | |||
void transpose_selected ( int n ); | |||
tick_t selection_min ( void ) const; | |||
tick_t selection_max ( void ) const; | |||
void move_selected ( tick_t tick ); | |||
void nudge_selected ( long o ); | |||
void push_selection ( void ); | |||
void pop_selection ( void ); | |||
void selected_velocity ( int v ); | |||
bool verify ( void ) const; | |||
void link ( event *on ); | |||
void insert_time ( tick_t start, tick_t l ); | |||
void delete_time ( tick_t start, tick_t end ); | |||
void relink ( void ); | |||
void sort ( event *e ); | |||
void sort ( void ); | |||
void move ( event *e, long o ); | |||
bool empty ( void ) const; | |||
size_t size ( void ) const; | |||
void append ( event *e ); | |||
void mix ( event *ne ); | |||
void hi_lo_note ( int *hi, int *lo ) const; | |||
void rewrite_selected ( int from, int to ); | |||
void selected_hi_lo_note ( int *hi, int *lo ) const; | |||
event_list & operator= ( const event_list &rhs ); | |||
event_list & operator= ( const list <midievent> &rhs ); | |||
event *operator[] ( unsigned int index ); | |||
// friend class event; | |||
}; | |||
} |
@@ -1,221 +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 "midievent.H" | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <stdio.h> | |||
#include "debug.h" | |||
namespace MIDI | |||
{ | |||
static const char *opcode_names[] = | |||
{ | |||
"Note Off", | |||
"Note On", | |||
"Aftertouch", | |||
"Control Change", | |||
"Program Change", | |||
"Channel Pressure", | |||
"Pitch Wheel" | |||
}; | |||
midievent::midievent ( void ) | |||
{ | |||
_sysex = NULL; | |||
_timestamp = 0; | |||
_data.status = NOTE_OFF; | |||
_data.msb = _data.lsb = 0; | |||
} | |||
midievent::~midievent ( void ) | |||
{ | |||
if ( _sysex ) | |||
delete _sysex; | |||
_sysex = NULL; | |||
} | |||
int | |||
midievent::pitch ( void ) const | |||
{ | |||
return ((_data.msb << 7) | _data.lsb) - 0x2000; | |||
} | |||
void | |||
midievent::pitch ( int n ) | |||
{ | |||
n += 0x2000; | |||
_data.lsb = n & 0x7F; | |||
_data.msb = (n >> 7) & 0x7F; | |||
} | |||
void | |||
midievent::data ( byte_t D1, byte_t D2 ) | |||
{ | |||
_data.lsb = D1 & 0x7F; | |||
_data.msb = D2 & 0x7F; | |||
} | |||
void | |||
midievent::data ( byte_t *D1, byte_t *D2 ) const | |||
{ | |||
*D1 = _data.lsb; | |||
*D2 = _data.msb; | |||
} | |||
void | |||
midievent::raw ( byte_t *p, int l) const | |||
{ | |||
memcpy( p, &_data, l ); | |||
} | |||
int | |||
midievent::size ( void ) const | |||
{ | |||
return midievent::event_size( opcode() ); | |||
} | |||
void | |||
midievent::note_velocity ( int vel ) | |||
{ | |||
_data.msb = vel & 0x7F; | |||
} | |||
unsigned char | |||
midievent::note ( void ) const | |||
{ | |||
return _data.lsb; | |||
} | |||
void | |||
midievent::note ( char note ) | |||
{ | |||
_data.lsb = note & 0x7F; | |||
} | |||
unsigned char | |||
midievent::note_velocity ( void ) const | |||
{ | |||
return _data.msb; | |||
} | |||
bool | |||
midievent::is_same_note ( midievent * e ) const | |||
{ | |||
return channel() == e->channel() && note() == e->note(); | |||
} | |||
/** get name from opcode */ | |||
const char * | |||
midievent::name ( void ) const | |||
{ | |||
return opcode_names[ (opcode() >> 4) - 8 ]; | |||
} | |||
/** get opcode from name */ | |||
int | |||
midievent::name ( const char *name ) const | |||
{ | |||
for ( unsigned int i = elementsof( opcode_names ); i--; ) | |||
if ( ! strcmp( name, opcode_names[ i ] ) ) | |||
return (i + 8) << 4; | |||
return -1; | |||
} | |||
/** print event in hexadecimal */ | |||
void | |||
midievent::print ( void ) const | |||
{ | |||
printf( "[%06f] %02X %02X %02X\n", | |||
_timestamp, | |||
_data.status, | |||
_data.lsb, | |||
_data.msb ); | |||
} | |||
/** print event in english/decimal */ | |||
void | |||
midievent::pretty_print ( void ) const | |||
{ | |||
printf( | |||
"[%06f] %-15s c: %2d d1: %3d d2: %3d\n", | |||
_timestamp, | |||
name(), | |||
channel(), | |||
_data.lsb, | |||
_data.msb ); | |||
} | |||
/*********/ | |||
/* Sysex */ | |||
/*********/ | |||
midievent::sysex::sysex ( void ) | |||
{ | |||
_data = NULL; | |||
_size = 0; | |||
_alloc = 0; | |||
} | |||
midievent::sysex::~sysex ( void ) | |||
{ | |||
if ( _data ) | |||
free( _data ); | |||
_data = NULL; | |||
} | |||
/** add bytes to sysex message */ | |||
void | |||
midievent::sysex::append ( byte_t *data, size_t size ) | |||
{ | |||
if ( _size + size > _alloc ) | |||
_data = (byte_t *)realloc( _data, _alloc += 256 ); | |||
memcpy( data + _size, data, size ); | |||
_size += size; | |||
} | |||
/** return SysEx data */ | |||
const byte_t * | |||
midievent::sysex::data ( void ) const | |||
{ | |||
return _data; | |||
} | |||
long | |||
midievent::sysex::size ( void ) const | |||
{ | |||
return _size; | |||
} | |||
bool | |||
midievent::operator== ( const midievent &rhs ) const | |||
{ | |||
return _timestamp == rhs._timestamp && | |||
! bcmp( (void*)&_data, (void*)&rhs._data, size() ); | |||
} | |||
} |
@@ -1,237 +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. */ | |||
/*******************************************************************************/ | |||
/* this class represents a single raw MIDI event plus a timestamp */ | |||
#pragma once | |||
#include "types.h" | |||
#include <stdlib.h> | |||
namespace MIDI | |||
{ | |||
class midievent | |||
{ | |||
public: | |||
class sysex { | |||
size_t _size, _alloc; | |||
byte_t *_data; | |||
public: | |||
sysex ( void ); | |||
~sysex ( void ); | |||
void append ( byte_t *data, size_t size ); | |||
const byte_t * data ( void ) const; | |||
long size ( void ) const; | |||
}; | |||
private: | |||
sysex *_sysex; | |||
tick_t _timestamp; /* in ticks */ | |||
struct { | |||
byte_t status, /* full status byte */ | |||
lsb, /* data 1 */ | |||
msb; /* data 2 */ | |||
} _data; | |||
public: | |||
static inline int | |||
event_size ( byte_t op ) | |||
{ | |||
switch ( op ) | |||
{ | |||
case NOTE_ON: case NOTE_OFF: case AFTERTOUCH: | |||
case CONTROL_CHANGE: case PITCH_WHEEL: | |||
return 3; | |||
case PROGRAM_CHANGE: case CHANNEL_PRESSURE: | |||
return 2; | |||
default: | |||
return 1; | |||
} | |||
}; | |||
/* define MIDI status bytes */ | |||
enum { | |||
STATUS_BIT = 0x80, | |||
NOTE_OFF = 0x80, | |||
NOTE_ON = 0x90, | |||
AFTERTOUCH = 0xA0, | |||
CONTROL_CHANGE = 0xB0, | |||
PROGRAM_CHANGE = 0xC0, | |||
CHANNEL_PRESSURE = 0xD0, | |||
PITCH_WHEEL = 0xE0, | |||
CLEAR_CHAN_MASK = 0xF0, | |||
MIDI_CLOCK = 0xF8, | |||
SYSEX = 0xF0, | |||
SYSEX_END = 0xF7, | |||
META = 0xFF | |||
}; | |||
midievent ( void ); | |||
virtual ~midievent ( void ); | |||
tick_t timestamp ( void ) const; | |||
void timestamp ( tick_t time ); | |||
void status ( byte_t status ); | |||
byte_t status ( void ) const; | |||
void channel ( byte_t channel ); | |||
byte_t channel ( void ) const; | |||
byte_t opcode ( void ) const; | |||
void opcode ( byte_t o ); | |||
void lsb ( byte_t n ); | |||
void msb ( byte_t n ); | |||
int lsb ( void ) const; | |||
int msb ( void ) const; | |||
int pitch ( void ) const; | |||
void pitch ( int n ); | |||
void data ( byte_t D1, byte_t D2 ); | |||
void data ( byte_t *D1, byte_t *D2 ) const; | |||
void raw ( byte_t *p, int l) const; | |||
int size ( void ) const; | |||
void note_velocity ( int vel ); | |||
bool is_note_on ( void ) const; | |||
bool is_note_off ( void ) const; | |||
virtual unsigned char note ( void ) const; | |||
virtual void note ( char note ); | |||
unsigned char note_velocity ( void ) const; | |||
bool is_same_note ( midievent * e ) const; | |||
const char * name ( void ) const; | |||
int name ( const char *name ) const; | |||
void print ( void ) const; | |||
void pretty_print ( void ) const; | |||
bool operator< ( const midievent &rhs ) const; | |||
bool operator>= ( const midievent &rhs ) const; | |||
bool operator== ( const midievent &rhs ) const; | |||
}; | |||
/**********************/ | |||
/* Inlined accessors */ | |||
/**********************/ | |||
inline tick_t | |||
midievent::timestamp ( void ) const | |||
{ | |||
return _timestamp; | |||
} | |||
inline void | |||
midievent::timestamp ( tick_t time ) | |||
{ | |||
_timestamp = time; | |||
} | |||
inline void | |||
midievent::status ( byte_t status ) | |||
{ | |||
_data.status = status; | |||
} | |||
inline byte_t | |||
midievent::status ( void ) const | |||
{ | |||
return _data.status; | |||
} | |||
inline void | |||
midievent::channel ( byte_t channel ) | |||
{ | |||
_data.status = (_data.status & 0xF0) | (channel & 0x0F); | |||
} | |||
inline byte_t | |||
midievent::channel ( void ) const | |||
{ | |||
return _data.status & 0x0F; | |||
} | |||
inline byte_t | |||
midievent::opcode ( void ) const | |||
{ | |||
return _data.status & 0xF0; | |||
} | |||
inline void | |||
midievent::opcode ( byte_t opcode ) | |||
{ | |||
_data.status = (_data.status & 0x0F) | (opcode & 0xF0); | |||
} | |||
inline void | |||
midievent::lsb ( byte_t n ) | |||
{ | |||
_data.lsb = n & 0x7F; | |||
} | |||
inline void | |||
midievent::msb ( byte_t n ) | |||
{ | |||
_data.msb = n & 0x7F; | |||
} | |||
inline int | |||
midievent::lsb ( void ) const | |||
{ | |||
return _data.lsb; | |||
} | |||
inline int | |||
midievent::msb ( void ) const | |||
{ | |||
return _data.msb; | |||
} | |||
inline bool | |||
midievent::is_note_on ( void ) const | |||
{ | |||
return (opcode() == NOTE_ON); | |||
} | |||
inline bool | |||
midievent::is_note_off ( void ) const | |||
{ | |||
return (opcode() == NOTE_OFF); | |||
} | |||
inline bool | |||
midievent::operator< ( const midievent &rhs ) const | |||
{ | |||
return _timestamp < rhs._timestamp; | |||
} | |||
inline bool | |||
midievent::operator>= ( const midievent &rhs ) const | |||
{ | |||
return _timestamp >= rhs._timestamp; | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* Copyright (C) 2007,2008 Jonathan Moore Liles */ | |||
/* */ | |||
/* This program is free software; you can redistribute it and/or modify it */ | |||
/* under the terms of the GNU General Public License as published by the */ | |||
/* Free Software Foundation; either version 2 of the License, or (at your */ | |||
/* option) any later version. */ | |||
/* */ | |||
/* This program is distributed in the hope that it will be useful, but WITHOUT */ | |||
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ | |||
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ | |||
/* more details. */ | |||
/* */ | |||
/* You should have received a copy of the GNU General Public License along */ | |||
/* with This program; see the file COPYING. If not,write to the Free Software */ | |||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
typedef unsigned char byte_t; | |||
typedef double tick_t; | |||
typedef unsigned int uint; | |||
#define elementsof(x) (sizeof((x)) / sizeof((x)[0])) | |||
@@ -1,81 +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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include <pthread.h> | |||
const pthread_mutex_t _mutex_initializer = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; | |||
class Mutex | |||
{ | |||
pthread_mutex_t _lock; | |||
public: | |||
Mutex ( ) | |||
{ | |||
// pthread_mutex_init( &_lock, NULL ); | |||
_lock = _mutex_initializer; | |||
} | |||
virtual ~Mutex ( ) | |||
{ | |||
pthread_mutex_destroy( &_lock ); | |||
} | |||
void | |||
lock ( void ) | |||
{ | |||
pthread_mutex_lock( &_lock ); | |||
} | |||
void | |||
unlock ( void ) | |||
{ | |||
pthread_mutex_unlock( &_lock ); | |||
} | |||
bool | |||
trylock ( void ) | |||
{ | |||
return pthread_mutex_trylock( &_lock ) == 0; | |||
} | |||
}; | |||
class Locker | |||
{ | |||
Mutex &_lock; | |||
public: | |||
Locker ( Mutex & lock ) : _lock( lock ) | |||
{ | |||
_lock.lock(); | |||
} | |||
~Locker ( ) | |||
{ | |||
_lock.unlock(); | |||
} | |||
}; |
@@ -1,298 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* 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, | |||
1, /* api_major_version */ | |||
0, /* 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; | |||
} | |||
}; |
@@ -1,109 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* 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 ); | |||
}; | |||
}; |
@@ -1,270 +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. */ | |||
/*******************************************************************************/ | |||
/* General DSP related functions. */ | |||
#include "dsp.h" | |||
#include "string.h" // for memset. | |||
#include <stdlib.h> | |||
static const int ALIGNMENT = 16; | |||
#ifdef HAS_BUILTIN_ASSUME_ALIGNED | |||
#define assume_aligned(x) __builtin_assume_aligned(x,ALIGNMENT) | |||
#else | |||
#define assume_aligned(x) (x) | |||
#endif | |||
sample_t * | |||
buffer_alloc ( nframes_t size ) | |||
{ | |||
void *p; | |||
posix_memalign( &p, ALIGNMENT, size * sizeof( sample_t ) ); | |||
return (sample_t*)p; | |||
} | |||
void | |||
buffer_apply_gain ( sample_t * __restrict__ buf, nframes_t nframes, float g ) | |||
{ | |||
sample_t * buf_ = (sample_t*) assume_aligned(buf); | |||
if ( g == 1.0f ) | |||
return; | |||
for ( nframes_t i = 0; i < nframes; i++ ) | |||
buf_[i] *= g; | |||
} | |||
void | |||
buffer_apply_gain_unaligned ( sample_t * __restrict__ buf, nframes_t nframes, float g ) | |||
{ | |||
if ( g == 1.0f ) | |||
return; | |||
for ( nframes_t i = 0; i < nframes; i++ ) | |||
buf[i] *= g; | |||
} | |||
void | |||
buffer_apply_gain_buffer ( sample_t * __restrict__ buf, const sample_t * __restrict__ gainbuf, nframes_t nframes ) | |||
{ | |||
sample_t * buf_ = (sample_t*) assume_aligned(buf); | |||
const sample_t * gainbuf_ = (const sample_t*) assume_aligned(gainbuf); | |||
for ( nframes_t i = 0; i < nframes; i++ ) | |||
buf_[i] *= gainbuf_[i]; | |||
} | |||
void | |||
buffer_copy_and_apply_gain_buffer ( sample_t * __restrict__ dst, const sample_t * __restrict__ src, const sample_t * __restrict__ gainbuf, nframes_t nframes ) | |||
{ | |||
sample_t * dst_ = (sample_t*) assume_aligned(dst); | |||
const sample_t * src_ = (const sample_t*) assume_aligned(src); | |||
const sample_t * gainbuf_ = (const sample_t*) assume_aligned(gainbuf); | |||
for ( nframes_t i = 0; i < nframes; i++ ) | |||
dst_[i] = src_[i] * gainbuf_[i]; | |||
} | |||
void | |||
buffer_mix ( sample_t * __restrict__ dst, const sample_t * __restrict__ src, nframes_t nframes ) | |||
{ | |||
sample_t * dst_ = (sample_t*) assume_aligned(dst); | |||
const sample_t * src_ = (const sample_t*) assume_aligned(src); | |||
for ( nframes_t i = 0; i < nframes; i++ ) | |||
dst_[i] += src_[i]; | |||
} | |||
void | |||
buffer_mix_with_gain ( sample_t * __restrict__ dst, const sample_t * __restrict__ src, nframes_t nframes, float g ) | |||
{ | |||
sample_t * dst_ = (sample_t*) assume_aligned(dst); | |||
const sample_t * src_ = (const sample_t*) assume_aligned(src); | |||
for ( nframes_t i = 0; i < nframes; i++ ) | |||
dst_[i] += src_[i] * g; | |||
} | |||
void | |||
buffer_interleave_one_channel ( sample_t * __restrict__ dst, const sample_t * __restrict__ src, int channel, int channels, nframes_t nframes ) | |||
{ | |||
dst += channel; | |||
while ( nframes-- ) | |||
{ | |||
*dst = *(src++); | |||
dst += channels; | |||
} | |||
} | |||
void | |||
buffer_interleave_one_channel_and_mix ( sample_t *__restrict__ dst, const sample_t * __restrict__ src, int channel, int channels, nframes_t nframes ) | |||
{ | |||
dst += channel; | |||
while ( nframes-- ) | |||
{ | |||
*dst += *(src++); | |||
dst += channels; | |||
} | |||
} | |||
void | |||
buffer_deinterleave_one_channel ( sample_t * __restrict__ dst, const sample_t * __restrict__ src, int channel, int channels, nframes_t nframes ) | |||
{ | |||
src += channel; | |||
while ( nframes-- ) | |||
{ | |||
*(dst++) = *src; | |||
src += channels; | |||
} | |||
} | |||
void | |||
buffer_interleaved_mix ( sample_t *__restrict__ dst, const sample_t * __restrict__ src, int dst_channel, int src_channel, int dst_channels, int src_channels, nframes_t nframes ) | |||
{ | |||
sample_t * dst_ = (sample_t*) assume_aligned(dst); | |||
const sample_t * src_ = (const sample_t*) assume_aligned(src); | |||
dst_ += dst_channel; | |||
src_ += src_channel; | |||
while ( nframes-- ) | |||
{ | |||
*dst_ += *src_; | |||
dst_ += dst_channels; | |||
src_ += src_channels; | |||
} | |||
} | |||
void | |||
buffer_interleaved_copy ( sample_t *__restrict__ dst, const sample_t * __restrict__ src, int dst_channel, int src_channel, int dst_channels, int src_channels, nframes_t nframes ) | |||
{ | |||
sample_t * dst_ = (sample_t*) assume_aligned(dst); | |||
const sample_t * src_ = (const sample_t*) assume_aligned(src); | |||
dst_ += dst_channel; | |||
src_ += src_channel; | |||
while ( nframes-- ) | |||
{ | |||
*dst_ = *src_; | |||
dst_ += dst_channels; | |||
src_ += src_channels; | |||
} | |||
} | |||
void | |||
buffer_fill_with_silence ( sample_t *buf, nframes_t nframes ) | |||
{ | |||
memset( buf, 0, nframes * sizeof( sample_t ) ); | |||
} | |||
bool | |||
buffer_is_digital_black ( const sample_t *buf, nframes_t nframes ) | |||
{ | |||
while ( nframes-- ) | |||
{ | |||
if (! *(buf++) ) | |||
continue; | |||
return false; | |||
} | |||
return true; | |||
} | |||
float | |||
buffer_get_peak ( const sample_t * __restrict__ buf, nframes_t nframes ) | |||
{ | |||
const sample_t * buf_ = (const sample_t*) assume_aligned(buf); | |||
float pmax = 0.0f; | |||
float pmin = 0.0f; | |||
for ( nframes_t i = 0; i < nframes; i++ ) | |||
{ | |||
pmax = buf_[i] > pmax ? buf_[i] : pmax; | |||
pmin = buf_[i] < pmin ? buf_[i] : pmin; | |||
} | |||
pmax = fabsf(pmax); | |||
pmin = fabsf(pmin); | |||
return pmax > pmin ? pmax : pmin; | |||
} | |||
void | |||
buffer_copy ( sample_t * __restrict__ dst, const sample_t * __restrict__ src, nframes_t nframes ) | |||
{ | |||
memcpy( dst, src, nframes * sizeof( sample_t ) ); | |||
} | |||
void | |||
buffer_copy_and_apply_gain ( sample_t * __restrict__ dst, const sample_t * __restrict__ src, nframes_t nframes, float gain ) | |||
{ | |||
memcpy( dst, src, nframes * sizeof( sample_t ) ); | |||
buffer_apply_gain( dst, nframes, gain ); | |||
} | |||
void | |||
Value_Smoothing_Filter::sample_rate ( nframes_t n ) | |||
{ | |||
const float FS = n; | |||
const float T = 0.05f; | |||
w = _cutoff / (FS * T); | |||
} | |||
bool | |||
Value_Smoothing_Filter::apply( sample_t * __restrict__ dst, nframes_t nframes, float gt ) | |||
{ | |||
sample_t * dst_ = (sample_t*) assume_aligned(dst); | |||
const float a = 0.07f; | |||
const float b = 1 + a; | |||
const float gm = b * gt; | |||
float g1 = this->g1; | |||
float g2 = this->g2; | |||
if ( target_reached(gt) ) | |||
return false; | |||
for (nframes_t i = 0; i < nframes; i++) | |||
{ | |||
g1 += w * (gm - g1 - a * g2); | |||
g2 += w * (g1 - g2); | |||
dst_[i] = g2; | |||
} | |||
if ( fabsf( gt - g2 ) < 0.0001f ) | |||
g2 = gt; | |||
this->g1 = g1; | |||
this->g2 = g2; | |||
return true; | |||
} |
@@ -1,88 +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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include "JACK/Client.H" | |||
#include <math.h> | |||
sample_t *buffer_alloc ( nframes_t size ); | |||
void buffer_apply_gain ( sample_t *buf, nframes_t nframes, float g ); | |||
void buffer_apply_gain_unaligned ( sample_t *buf, nframes_t nframes, float g ); | |||
void buffer_apply_gain_buffer ( sample_t *buf, const sample_t *gainbuf, nframes_t nframes ); | |||
void buffer_copy_and_apply_gain_buffer ( sample_t *dst, const sample_t *src, const sample_t *gainbuf, nframes_t nframes ); | |||
void buffer_mix ( sample_t *dst, const sample_t *src, nframes_t nframes ); | |||
void buffer_mix_with_gain ( sample_t *dst, const sample_t *src, nframes_t nframes, float g ); | |||
void buffer_interleave_one_channel ( sample_t *dst, const sample_t *src, int channel, int channels, nframes_t nframes ); | |||
void buffer_interleave_one_channel_and_mix ( sample_t *dst, const sample_t *src, int channel, int channels, nframes_t nframes ); | |||
void buffer_deinterleave_one_channel ( sample_t *dst, const sample_t *src, int channel, int channels, nframes_t nframes ); | |||
void buffer_interleaved_mix ( sample_t *__restrict__ dst, const sample_t * __restrict__ src, int dst_channel, int src_channel, int dst_channels, int src_channels, nframes_t nframes ); | |||
void buffer_interleaved_copy ( sample_t *__restrict__ dst, const sample_t * __restrict__ src, int dst_channel, int src_channel, int dst_channels, int src_channels, nframes_t nframes ); | |||
void buffer_fill_with_silence ( sample_t *buf, nframes_t nframes ); | |||
bool buffer_is_digital_black ( const sample_t *buf, nframes_t nframes ); | |||
float buffer_get_peak ( const sample_t *buf, nframes_t nframes ); | |||
void buffer_copy ( sample_t *dst, const sample_t *src, nframes_t nframes ); | |||
void buffer_copy_and_apply_gain ( sample_t *dst, const sample_t *src, nframes_t nframes, float gain ); | |||
class Value_Smoothing_Filter | |||
{ | |||
float w, g1, g2; | |||
float _cutoff; | |||
public: | |||
Value_Smoothing_Filter ( ) | |||
{ | |||
g1 = g2 = 0; | |||
_cutoff = 10.0f; | |||
} | |||
void cutoff ( float v ) { _cutoff = v; } | |||
void sample_rate ( nframes_t v ); | |||
inline bool target_reached ( float gt ) const { return gt == g2; } | |||
bool apply ( sample_t *dst, nframes_t nframes, float target ); | |||
}; | |||
static inline float interpolate_cubic ( const float fr, const float inm1, const float in, const float inp1, const float inp2) | |||
{ | |||
return in + 0.5f * fr * (inp1 - inm1 + | |||
fr * (4.0f * inp1 + 2.0f * inm1 - 5.0f * in - inp2 + | |||
fr * (3.0f * (in - inp1) - inm1 + inp2))); | |||
} | |||
// from SWH plugins. | |||
// Convert a value in dB's to a coefficent | |||
#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) | |||
#define CO_DB(v) (20.0f * log10f(v)) | |||
#define DEG2RAD 0.01745329251f | |||
#define ONEOVERSQRT2 0.70710678118f | |||
#ifndef likely | |||
#define likely(x) __builtin_expect(x,1) | |||
#endif | |||
#ifndef unlikely | |||
#define unlikely(x) __builtin_expect(x,0) | |||
#endif |
@@ -1,91 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* 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 <string.h> | |||
#include <stdio.h> | |||
void unescape_url ( char *url ) | |||
{ | |||
char *r, *w; | |||
r = w = url; | |||
for ( ; *r; r++, w++ ) | |||
{ | |||
if ( *r == '%' ) | |||
{ | |||
char data[3] = { *(r + 1), *(r + 2), 0 }; | |||
int c; | |||
sscanf( data, "%2X", &c ); | |||
*w = c; | |||
r += 2; | |||
} | |||
else | |||
*w = *r; | |||
} | |||
*w = 0; | |||
} | |||
char *escape_url ( const char *url ) | |||
{ | |||
const char *s; | |||
char *w; | |||
char r[1024]; | |||
s = url; | |||
w = r; | |||
for ( ; *s && w < r + sizeof( r ); s++, w++ ) | |||
{ | |||
switch ( *s ) | |||
{ | |||
case '<': | |||
case '>': | |||
case '%': | |||
// liblo doesn't like these in method names | |||
case '[': | |||
case ']': | |||
case '{': | |||
case '}': | |||
case '?': | |||
case ',': | |||
case '#': | |||
case '*': | |||
case ' ': | |||
sprintf( w, "%%%2X", *s ); | |||
w += 2; | |||
break; | |||
default: | |||
*w = *s; | |||
break; | |||
} | |||
} | |||
*w = 0; | |||
return strdup( r ); | |||
} |
@@ -1,21 +0,0 @@ | |||
/*******************************************************************************/ | |||
/* 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. */ | |||
/*******************************************************************************/ | |||
void unescape_url ( char *url ); | |||
char * escape_url ( const char *url ); |
@@ -1,25 +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. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include <jack/jack.h> | |||
typedef jack_nframes_t nframes_t; | |||
typedef float sample_t; |
@@ -7,23 +7,14 @@ def configure(conf): | |||
pass | |||
def build(bld): | |||
#Thread is needed by OSC/Endpoint | |||
#File is for nsmd lockfiles | |||
bld.stlib( | |||
source = ''' | |||
JACK/Client.C | |||
JACK/Port.C | |||
Log_Entry.C | |||
Loggable.C | |||
NSM/Client.C | |||
OSC/Endpoint.C | |||
Thread.C | |||
debug.C | |||
dsp.C | |||
file.C | |||
MIDI/midievent.C | |||
string_util.C | |||
MIDI/event_list.C | |||
MIDI/event.C | |||
MIDI/midievent.C | |||
''', | |||
includes = '.', | |||
export_incdirs = [ '.', 'nonlib'], | |||
@@ -19,6 +19,7 @@ | |||
#define __MODULE__ "nsmd" | |||
//debug.C has only one function that gets used multiple times by debug.H and for logging and printing | |||
#include "debug.h" | |||
#ifndef _GNU_SOURCE | |||