@@ -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 | pass | ||||
def build(bld): | def build(bld): | ||||
#Thread is needed by OSC/Endpoint | |||||
#File is for nsmd lockfiles | |||||
bld.stlib( | bld.stlib( | ||||
source = ''' | source = ''' | ||||
JACK/Client.C | |||||
JACK/Port.C | |||||
Log_Entry.C | |||||
Loggable.C | |||||
NSM/Client.C | |||||
OSC/Endpoint.C | OSC/Endpoint.C | ||||
Thread.C | Thread.C | ||||
debug.C | debug.C | ||||
dsp.C | |||||
file.C | file.C | ||||
MIDI/midievent.C | |||||
string_util.C | |||||
MIDI/event_list.C | |||||
MIDI/event.C | |||||
MIDI/midievent.C | |||||
''', | ''', | ||||
includes = '.', | includes = '.', | ||||
export_incdirs = [ '.', 'nonlib'], | export_incdirs = [ '.', 'nonlib'], | ||||
@@ -19,6 +19,7 @@ | |||||
#define __MODULE__ "nsmd" | #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" | #include "debug.h" | ||||
#ifndef _GNU_SOURCE | #ifndef _GNU_SOURCE | ||||