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