@@ -90,6 +90,35 @@ unescape ( char *s ) | |||||
*r = '\0'; | *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 ); | |||||
return r2; | |||||
} | |||||
/** sigh. parse a string of ":name value :name value" pairs into an | /** sigh. parse a string of ":name value :name value" pairs into an | ||||
* array of strings, one per pair */ | * array of strings, one per pair */ | ||||
// FIXME: doesn't handle the case of :name ":foo bar", nested quotes | // FIXME: doesn't handle the case of :name ":foo bar", nested quotes | ||||
@@ -67,6 +67,8 @@ public: | |||||
void get ( int n, const char **name, const char **value ) const; | void get ( int n, const char **name, const char **value ) const; | ||||
char **sa ( void ); | char **sa ( void ); | ||||
char *print ( void ) const; | |||||
/* #define ADD ( type, format, exp ) \ */ | /* #define ADD ( type, format, exp ) \ */ | ||||
/* void add ( const char *name, type v ) \ */ | /* void add ( const char *name, type v ) \ */ | ||||
/* { \ */ | /* { \ */ | ||||
@@ -50,6 +50,8 @@ off_t Loggable::_undo_offset = 0; | |||||
size_t Loggable::_loggables_size = 0; | size_t Loggable::_loggables_size = 0; | ||||
Loggable ** Loggable::_loggables; | Loggable ** Loggable::_loggables; | ||||
std::map <unsigned int, Log_Entry *> Loggable::_loggables_unjournaled; | |||||
std::map <std::string, create_func*> Loggable::_class_map; | std::map <std::string, create_func*> Loggable::_class_map; | ||||
std::queue <char *> Loggable::_transaction; | std::queue <char *> Loggable::_transaction; | ||||
@@ -61,6 +63,14 @@ void *Loggable::_snapshot_callback_arg = NULL; | |||||
Loggable::~Loggable ( ) | |||||
{ | |||||
_loggables[ _id - 1 ] = NULL; | |||||
} | |||||
/** ensure that _loggables array is big enough for /n/ elements */ | /** ensure that _loggables array is big enough for /n/ elements */ | ||||
void | void | ||||
Loggable::ensure_size ( size_t n ) | Loggable::ensure_size ( size_t n ) | ||||
@@ -121,6 +131,8 @@ Loggable::open ( const char *filename ) | |||||
return false; | return false; | ||||
} | } | ||||
load_unjournaled_state(); | |||||
if ( newer( "snapshot", filename ) ) | if ( newer( "snapshot", filename ) ) | ||||
{ | { | ||||
MESSAGE( "Loading snapshot" ); | MESSAGE( "Loading snapshot" ); | ||||
@@ -146,6 +158,33 @@ Loggable::open ( const char *filename ) | |||||
return true; | return true; | ||||
} | } | ||||
bool | |||||
Loggable::load_unjournaled_state ( void ) | |||||
{ | |||||
FILE *fp; | |||||
fp = fopen( "unjournaled", "r" ); | |||||
if ( ! fp ) | |||||
return false; | |||||
unsigned int id; | |||||
char buf[BUFSIZ]; | |||||
while ( fscanf( fp, "%X set %[^\n]\n", &id, buf ) == 2 ) | |||||
{ | |||||
Log_Entry *e = new Log_Entry( buf ); | |||||
_loggables_unjournaled[ id - 1 ] = e; | |||||
Loggable *l = Loggable::find( id ); | |||||
} | |||||
fclose( fp ); | |||||
return true; | |||||
} | |||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
@@ -214,11 +253,45 @@ Loggable::close ( void ) | |||||
} | } | ||||
} | } | ||||
save_unjournaled_state(); | |||||
_log_id = 0; | _log_id = 0; | ||||
return true; | return true; | ||||
} | } | ||||
/** save out unjournaled state for all loggables */ | |||||
bool | |||||
Loggable::save_unjournaled_state ( void ) | |||||
{ | |||||
/* FIXME: check for errors */ | |||||
FILE *fp = fopen( "unjournaled", "w" ); | |||||
/* write out the unjournaled state of all currently active | |||||
* loggables */ | |||||
for ( int i = 0; i < _log_id - 1; ++i ) | |||||
{ | |||||
Log_Entry *e = _loggables_unjournaled[ i ]; | |||||
if ( e ) | |||||
{ | |||||
char *s = e->print(); | |||||
fprintf( fp, "0x%X set %s\n", i + 1, s ); | |||||
free( s ); | |||||
} | |||||
} | |||||
/* write out the remembered state of inactive loggables. */ | |||||
fclose( fp ); | |||||
return true; | |||||
} | |||||
/** must be called after construction in create() methods */ | /** must be called after construction in create() methods */ | ||||
void | void | ||||
Loggable::update_id ( unsigned int id ) | Loggable::update_id ( unsigned int id ) | ||||
@@ -338,6 +411,12 @@ Loggable::do_this ( const char *s, bool reverse ) | |||||
/* create */ | /* create */ | ||||
Loggable *l = _class_map[ std::string( classname ) ]( e, id ); | Loggable *l = _class_map[ std::string( classname ) ]( e, id ); | ||||
l->log_create(); | l->log_create(); | ||||
/* we're now creating a loggable. Apply any unjournaled | |||||
* state it may have had in the past under this log ID */ | |||||
if ( _loggables_unjournaled[ id - 1 ] ) | |||||
l->set( *_loggables_unjournaled[ id - 1 ] ); | |||||
} | } | ||||
} | } | ||||
@@ -391,29 +470,6 @@ Loggable::undo ( void ) | |||||
_undo_offset = uo; | _undo_offset = uo; | ||||
} | } | ||||
/** Make all loggable ids consecutive. This invalidates any existing | |||||
* journal or snapshot, so you *must* write out a new one after | |||||
* performing this operation*/ | |||||
void | |||||
Loggable::compact_ids ( void ) | |||||
{ | |||||
unsigned int id = 0; | |||||
for ( unsigned int i = 0; i < _log_id; ++i ) | |||||
if ( _loggables[ i ] ) | |||||
{ | |||||
++id; | |||||
if ( _loggables[ id - 1 ] ) | |||||
continue; | |||||
_loggables[ id - 1 ] = _loggables[ i ]; | |||||
_loggables[ i ] = NULL; | |||||
_loggables[ id - 1 ]->_id = id; | |||||
} | |||||
_log_id = id; | |||||
} | |||||
/** write a snapshot of the current state of all loggable objects to | /** write a snapshot of the current state of all loggable objects to | ||||
* file handle /fp/ */ | * file handle /fp/ */ | ||||
bool | bool | ||||
@@ -468,7 +524,6 @@ Loggable::compact ( void ) | |||||
fseek( _fp, 0, SEEK_SET ); | fseek( _fp, 0, SEEK_SET ); | ||||
ftruncate( fileno( _fp ), 0 ); | ftruncate( fileno( _fp ), 0 ); | ||||
compact_ids(); | |||||
if ( ! snapshot( _fp ) ) | if ( ! snapshot( _fp ) ) | ||||
FATAL( "Could not write snapshot!" ); | FATAL( "Could not write snapshot!" ); | ||||
@@ -654,11 +709,37 @@ Loggable::log_create ( void ) const | |||||
Loggable::flush(); | 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_unjournaled[ _id - 1 ]; | |||||
if ( *le ) | |||||
delete *le; | |||||
if ( e->size() ) | |||||
{ | |||||
*le = e; | |||||
DMESSAGE( "logging %s", (*le)->print() ); | |||||
} | |||||
else | |||||
/* don't waste space on loggables with no unjournaled properties */ | |||||
*le = NULL; | |||||
} | |||||
/** Log object destruction. *Must* be called at the beginning of the | /** Log object destruction. *Must* be called at the beginning of the | ||||
* destructors of leaf classes */ | * destructors of leaf classes */ | ||||
void | void | ||||
Loggable::log_destroy ( void ) const | Loggable::log_destroy ( void ) const | ||||
{ | { | ||||
/* the unjournaled state may have changed: make a note of it. */ | |||||
record_unjournaled(); | |||||
if ( ! _fp ) | if ( ! _fp ) | ||||
/* tearing down... don't bother */ | /* tearing down... don't bother */ | ||||
return; | return; | ||||
@@ -73,6 +73,7 @@ class Loggable | |||||
static size_t _loggables_size; | static size_t _loggables_size; | ||||
static Loggable ** _loggables; | static Loggable ** _loggables; | ||||
static std::map <unsigned int, Log_Entry *> _loggables_unjournaled; | |||||
static std::map <std::string, create_func*> _class_map; | static std::map <std::string, create_func*> _class_map; | ||||
@@ -94,13 +95,14 @@ private: | |||||
static void ensure_size ( size_t n ); | static void ensure_size ( size_t n ); | ||||
void log_print( const Log_Entry *o, const Log_Entry *n ) const; | |||||
void log_print ( const Log_Entry *o, const Log_Entry *n ) const; | |||||
static void log ( const char *fmt, ... ); | static void log ( const char *fmt, ... ); | ||||
static void flush ( void ); | static void flush ( void ); | ||||
static bool snapshot( FILE * fp ); | |||||
static bool snapshot( const char *name ); | |||||
static bool snapshot ( FILE * fp ); | |||||
static bool snapshot ( const char *name ); | |||||
static bool save_unjournaled_state ( void ); | |||||
static bool replay ( FILE *fp ); | static bool replay ( FILE *fp ); | ||||
void init ( bool loggable=true ) | void init ( bool loggable=true ) | ||||
@@ -125,7 +127,8 @@ private: | |||||
/* not implemented */ | /* not implemented */ | ||||
const Loggable & operator= ( const Loggable &rhs ); | const Loggable & operator= ( const Loggable &rhs ); | ||||
static void compact_ids ( void ); | |||||
void record_unjournaled ( void ) const; | |||||
static bool load_unjournaled_state ( void ); | |||||
public: | public: | ||||
@@ -153,10 +156,7 @@ public: | |||||
void update_id ( unsigned int id ); | void update_id ( unsigned int id ); | ||||
virtual ~Loggable ( ) | |||||
{ | |||||
_loggables[ _id - 1 ] = NULL; | |||||
} | |||||
virtual ~Loggable ( ); | |||||
static | static | ||||
void | void | ||||
@@ -167,6 +167,10 @@ public: | |||||
/* log messages for journal */ | /* log messages for journal */ | ||||
virtual void get ( Log_Entry &e ) const = 0; | 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 void set ( Log_Entry &e ) = 0; | ||||
virtual const char *class_name ( void ) const = 0; | virtual const char *class_name ( void ) const = 0; | ||||
@@ -176,7 +176,6 @@ Project::close ( void ) | |||||
tle->save_timeline_settings(); | tle->save_timeline_settings(); | ||||
Loggable::close(); | Loggable::close(); | ||||
// write_info(); | // write_info(); | ||||
_is_open = false; | _is_open = false; | ||||
@@ -73,21 +73,25 @@ Track::Track ( ) : Fl_Group( 0, 0, 1, 1 ) | |||||
timeline->add_track( this ); | timeline->add_track( this ); | ||||
} | } | ||||
Track::~Track ( ) | Track::~Track ( ) | ||||
{ | { | ||||
Loggable::block_start(); | Loggable::block_start(); | ||||
/* must destroy sequences first to preserve proper log order */ | |||||
takes->clear(); | |||||
control->clear(); | |||||
annotation->clear(); | |||||
delete sequence(); | |||||
takes = NULL; | takes = NULL; | ||||
control = NULL; | control = NULL; | ||||
annotation = NULL; | annotation = NULL; | ||||
solo( false ); | |||||
Fl_Group::clear(); | |||||
log_destroy(); | log_destroy(); | ||||
/* ensure that soloing accounting is performed */ | |||||
solo( false ); | |||||
timeline->remove_track( this ); | timeline->remove_track( this ); | ||||
/* give up our ports */ | /* give up our ports */ | ||||
@@ -101,6 +105,7 @@ Track::~Track ( ) | |||||
Loggable::block_end(); | Loggable::block_end(); | ||||
} | } | ||||
#include "FL/Boxtypes.H" | #include "FL/Boxtypes.H" | ||||
void | void | ||||
@@ -258,6 +263,12 @@ Track::set ( Log_Entry &e ) | |||||
} | } | ||||
else if ( ! strcmp( s, ":show-all-takes" ) ) | else if ( ! strcmp( s, ":show-all-takes" ) ) | ||||
show_all_takes( atoi( v ) ); | show_all_takes( atoi( v ) ); | ||||
else if ( ! strcmp( s, ":solo" ) ) | |||||
solo( atoi( v ) ); | |||||
else if ( ! strcmp( s, ":mute" ) ) | |||||
mute( atoi( v ) ); | |||||
else if ( ! strcmp( s, ":arm" ) ) | |||||
armed( atoi( v ) ); | |||||
else if ( ! strcmp( s, ":sequence" ) ) | else if ( ! strcmp( s, ":sequence" ) ) | ||||
{ | { | ||||
int i; | int i; | ||||
@@ -290,11 +301,19 @@ Track::get ( Log_Entry &e ) const | |||||
e.add( ":name", _name ); | e.add( ":name", _name ); | ||||
e.add( ":sequence", sequence() ); | e.add( ":sequence", sequence() ); | ||||
e.add( ":selected", _selected ); | e.add( ":selected", _selected ); | ||||
e.add( ":color", (unsigned long)color()); | |||||
} | |||||
void | |||||
Track::get_unjournaled ( Log_Entry &e ) const | |||||
{ | |||||
e.add( ":height", size() ); | e.add( ":height", size() ); | ||||
e.add( ":inputs", input.size() ); | e.add( ":inputs", input.size() ); | ||||
e.add( ":outputs", output.size() ); | e.add( ":outputs", output.size() ); | ||||
e.add( ":color", (unsigned long)color()); | |||||
e.add( ":show-all-takes", _show_all_takes ); | e.add( ":show-all-takes", _show_all_takes ); | ||||
e.add( ":armed", armed() ); | |||||
e.add( ":mute", mute() ); | |||||
e.add( ":solo", solo() ); | |||||
} | } | ||||
void | void | ||||
@@ -104,6 +104,7 @@ private: | |||||
protected: | protected: | ||||
void get ( Log_Entry &e ) const; | void get ( Log_Entry &e ) const; | ||||
void get_unjournaled ( Log_Entry &e ) const; | |||||
void set ( Log_Entry &e ); | void set ( Log_Entry &e ); | ||||
public: | public: | ||||
@@ -177,11 +178,15 @@ public: | |||||
const char * name ( void ) const { return _name; } | const char * name ( void ) const { return _name; } | ||||
bool mute ( void ) const { return mute_button->value(); } | bool mute ( void ) const { return mute_button->value(); } | ||||
void mute ( bool b ) { mute_button->value( b ); } | |||||
bool solo ( void ) const { return solo_button->value(); } | bool solo ( void ) const { return solo_button->value(); } | ||||
void solo ( bool b ); | |||||
bool armed ( void ) const { return record_button->value(); } | bool armed ( void ) const { return record_button->value(); } | ||||
void armed ( bool b ) { record_button->value( b ); } | |||||
bool selected ( void ) const { return _selected; } | bool selected ( void ) const { return _selected; } | ||||
void solo ( bool b ); | |||||
static void cb_input_field ( Fl_Widget *w, void *v ); | static void cb_input_field ( Fl_Widget *w, void *v ); | ||||
void cb_input_field ( void ); | void cb_input_field ( void ); | ||||